0% found this document useful (0 votes)
340 views

Programming in Ada

This tutorial teaches the entire Ada 95 dialect of the Ada language. It is composed of 33 chapters which should be studied in order. The diligent student will modify the example program as it is studied. Completion of the second part will give the student the ability to use all of the capabilities of Ada.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
340 views

Programming in Ada

This tutorial teaches the entire Ada 95 dialect of the Ada language. It is composed of 33 chapters which should be studied in order. The diligent student will modify the example program as it is studied. Completion of the second part will give the student the ability to use all of the capabilities of Ada.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 763

ADA 95 TUTORIAL

This tutorial teaches the entire Ada 95 dialect of the Ada language. It is composed of 33 chapters
which should be studied in order since topics are introduced in a logical order and build upon topics
introduced in previous chapters. It is to the students benefit to download the source code for the
example programs, then compile and execute each program as it is studied. The diligent student will
modify the example program in some way, then recompile and execute it to see if he understands the
material studied for that program. This will provide the student with valuable experience using his
compiler.
The recommended method of study is to print the text for one or two chapters, download the example
programs, and study the material by loading the example programs in the compiler's editor for
viewing. Following successful completion of each chapter, additional chapters can be downloaded as
progress is made.
Note that completion of the first part of this tutorial will give the student the ability to write very
significant programs in Ada, but completion of the second part will give the student the ability to use
all of the capabilities of Ada.
Version 2.5 - February 1, 1998
The original for this page is located at http://www.swcp.com/~dodrill/a95doc/a95list.htm and is the
only fully authorized site for distribution of this tutorial. Many persons have downloaded one or more
of our tutorials for redistribution without our consent, and occasionally do not include all of the
components needed for the complete package. You can be assured that the tutorial will be complete
and up to date only at the home site, since we have no control over the actions of other web site
operators.
This tutorial is distributed as shareware which means that you do not have to pay to use it. However,
the author spent a good deal of time and financial resources to develop this tutorial and requests that
you share in the financial burden in a very small way, but only if you felt the tutorial was valuable to
you as an aid in learning to program in Ada. If you wish to remit a small payment to the author, full
instructions for doing so will be given by clicking the link below. I hope you find programming in
Ada to be rewarding and profitable. I personally think Ada is the best language to use for a large
project with more than a single programmer because of the careful interface checking done by the
compiler.
How to Remit Payment For this Tutorial!
Part 1 - Beginning Ada 95 Tutorial
Introduction - What is Ada and why study it?
Chapter 1 - Getting Started
Chapter 2 - Program Structure
Chapter 3 - The Integer Type Variable
Chapter 4 - Logical Compares & precedence
Chapter 5 - Control Structures
Chapter 6 - Additional Scalar Types
Chapter 7 - Derived Types
Chapter 8 - Subprograms
Chapter 9 - Blocks and Scope of variables
Chapter 10 - Arrays
Chapter 11 - The Character & String Type
Chapter 12 - Records
Chapter 13 - The Access Type Variable
Chapter 14 - Input/Output
Chapter 15 - Packages
Chapter 16 - Example Programs
Part 2 - Advanced Ada 95 Tutorial
Chapter 17 - Exceptions
Chapter 18 - Advanced Subprogram Topics
Chapter 19 - Advanced Array Topics
Chapter 20 - Advanced Record Topics
Chapter 21 - Advanced Packages & Private Types
Chapter 22 - Object Oriented Programming
Chapter 23 - More Object Oriented Programming
Chapter 24 - Binary Input/Output
Chapter 25 - Dynamic Allocation
Chapter 26 - Tasking
Chapter 27 - The Simple Rendezvous
Chapter 28 - The Conditional Rendezvous
Chapter 29 - Additional Tasking Topics
Chapter 30 - Generic Subprograms
Chapter 31- Generic Packages
Chapter 32 - Control of Representation
Chapter 33 - More Example Programs
The source code and the answers for part 1 should be kept in a separate directory from those given for
part 2 because some of the file names are repeated. This is done to illustrate evolution of software
techniques as additional constructs are studied. All of the HTML documentation, however, should be
placed in a single directory.
Download HTML Documentation for Ada 95 Part 1 - (a95html.zip) Download the Introduction and
Chapters 1 to 16 in one packed file. This file (about 132k) contains the 17 files mentioned (plus the
diagrams) which can be downloaded and unpacked for use locally. The content of this file is identical
to the content of those 17 files. There are no executable files in this group of files.
Source Code for Ada 95 Part 1 - (a95src1.zip) Download all example programs. This file (about 48k)
contains 86 source files which are all explained in the 16 chapters of text. There are no executable
files in this group of files.
Answers to Exercises for Ada 95 Part 1- (a95ans1.zip) Download the authors answers to all of the
programming exercises. This file (about 20k) contains 37 source files. There are no executable files
in this group of files.
Download HTML Documentation for Ada 95 Part 2- (a95htm2.zip) Download the Introduction and
Chapters 17 to 33 in one packed file. This file (about 144k) contains the 17 files mentioned (plus the
diagrams) which can be downloaded and unpacked for use locally. The content of this file is identical
to the content of those 17 files. There are no executable files in this group of files. These files when
unpacked, should be placed in the same directory as the documentation files from Part 1 of this
tutorial.
Source Code for Ada 95 Part 2- (a95src2.zip) Download all example programs. This file (about 67k)
contains 100 source files which are all explained in the 15 chapters of text. There are no executable
files in this group of files.
Answers to Exercises for Ada 95 Part 2- (a95ans2.zip) Download the authors answers to all of the
programming exercises. This file (about 29k) contains 39 source files. There are no executable files
in this group of files.
pkunzip executable - (pkunzip.exe) Download pkunzip.exe version 2.04 to unzip the source code.
This executable is pre-registered for your use in unzipping any Coronado Enterprises tutorial files. It
will unpack and generate the zipped files in the current directory and all will be ASCII source code
files. To unzip the source code files for Ada 95 part 1, execute the following DOS command;
pkunzip a95src1.zip
Or, to unzip the answers to programming exercises for Ada 95 part 1, execute the following DOS
command;
pkunzip a95ans1.zip
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Programming Language Turorials (C, C++,
Pascal, Etc.)
This used to be Gordon Dodrill's web site, where he stored many programming language tutorials. He
has since moved this information to a new site:
http://www.coronadoenterprises.com/
This old URL (the page you are on right now) is linked in many references around the web. So we
have set up this place-holder page to direct people to the new home of these language tutorials. Please
click the http://www.coronadoenterprises.com/ link to access the language tutorials.
Hosted
by
SWCP home page

Southwest
Cyberport
Ada 95 Tutorial - Introduction
INTRODUCTION
Welcome to the world of Ada 95, a relatively new programming language. Even though Ada has
been mandated by the government for certain projects in the past, the mandate has been lifted and
Ada is a powerful enough language that it will be successful in the marketplace based on its own
merits.
Following 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. Throughout this tutorial, the names Ada or Ada 95 will be used interchangeably to refer to Ada
95, and anytime reference is made to the original version, the name Ada 83 will be explicitly stated.
ADA IS A LARGE LANGUAGE
By its very nature, Ada is a large language that is relatively difficult to compile because it does so
many checks at compile time. It has been estimated that a fully validated Ada compiler requires about
50 man years to write and debug. This is an indication that there will not be a plethora of Ada
compilers, and they will not be small programs. In addition, the Department of Defense requires any
implementation that is called Ada, to be a complete system. It must comprise the entire core of the
Ada specification, but can also include any of several annexes defines as extensions to Ada. Much
more will be said about this later in the tutorial.
Due to the size of the Ada language, this tutorial will be divided into two parts. Part 1 (Beginning
Ada) will cover most of the essentials of Ada, and will instruct you in the use of Ada in much the
same way that you would use Pascal. If you complete only part 1, you will be able to write useful
programs in Ada, but you will lack the elegance of the advanced features of Ada which are covered in
Part 2 (Advanced Ada) of this tutorial. Since Ada was intended to be a large, flexible language, it
would be well for you to complete both parts of this tutorial.
ADA IS NOT JUST ANOTHER LANGUAGE
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.
HOW IS THIS TUTORIAL WRITTEN?
A good reference manual and a good tutorial are mutually exclusive entities, a reference manual
being terrible for learning the material, and a tutorial being very poor as a reference following the
learning cycle. This tutorial is written as a tutorial, and no effort was given toward making it a
reference manual. It is therefore imperative that the student studies the lessons in the order given in
the tutorial. Many new concepts, as taught in this tutorial, use concepts illustrated earlier, and the
earlier concepts must be understood in order to push profitably ahead.
The example programs are all complete programs that can be compiled and executed on any
computer with a validated Ada 95 compiler. None of the example programs are fragments, so you
have a complete program before you with all of the code needed to execute each new concept as it is
studied. Library naming has changed from Ada 83 to Ada 95 so very few of the example programs in
this tutorial will compile without error when using an Ada 83 compiler. The Ada 83 naming
conventions are compatible with Ada 95, but we felt it was better to use the new naming conventions
rather than the old.
WHAT IF YOU DON'T HAVE AN ADA COMPILER?
Following the code for each example program, you will find a list of the results, in the form of Ada
comments, that were obtained by executing the program with a validated Ada compiler. Actually, as
many as five different compilers were used to compile each of these files, so the results should be
reliable. The result of execution can be used for either of two purposes, the first being to check the
output of your compiler for conformance to the standard, and the second being to give you the
execution result if you don't have an Ada compiler. Occasionally, you may wish to see the results
without actually going through the compile, link, load, and run steps, so the listed output can be a
time saver in those cases.
Every attempt was made to illustrate good programming practice with every example program, with
the exception of two programs that are intended to show poor programming practice. As you study
the example programs in this tutorial, you will therefore be developing a good programming style
through observation.
WHO WAS THIS TUTORIAL WRITTEN FOR?
This tutorial was written especially for two groups of programmers, those with expertise in
FORTRAN, and those with expertise in a modern structured language such as Pascal, C, or C++. A
person with little or no programming experience will have a fairly difficult time working his way
through this tutorial because of the size and complexity of Ada, but with enough patience, even this
person can learn Ada by carefully studying this tutorial.
The first group probably consists of mature programmers, those that have been in the industry for
many years and with experience using FORTRAN for a large number of those years. This author is a
member of this group, having begun programming in FORTRAN II in 1961. The experienced
FORTRAN programmer will find the concept of the data type to be a new and seemingly
troublesome addition to a programming language, so careful attention is paid to explaining the details
of this concept and other concepts that would be new to him.
The second group would consist of those programmers that have experience with a modern structured
language, such as Pascal, and already understand the concept of the data type, but find the newer
additions to Ada totally foreign. This author is also a member of the second group, having
programmed in Pascal, C, and C++ for about fifteen years now, with Ada being added to the list
about eleven years ago.
WHICH GROUP ARE YOU?
If you are an experienced FORTRAN programmer, you will need nearly all of the details given in
each chapter, but if you are of the modern structured language school, you will only need a small part
of the details in some of the chapters, and can therefore read them very quickly. A word of caution is
in order however, because some of the details seem to be very much like Pascal but in reality are very
different, so it will pay to look closely at even those things that you are already proficient with.
The experienced C programmer will probably be a bit frustrated with the attention to details required
by the Ada compiler. You will not have your favorite "tricks" available to fool the compiler into
doing something out of the ordinary. The Ada compiler cannot be fooled.
NOTE ON COMPUTER WORDSIZE
The majority of computers available for common use today use a 32 bit word, so it is expected that
the majority of users will be using a 32 bit system. For that reason, in those areas of this tutorial
where word size matters, 32 bits will be assumed and the results of execution will reflect that size. It
you are using a system that consists of 16 bits, or any other size for that matter, it should be easy for
you to ascertain the correct result for your system. Most of the places where it matters is where we
discuss the limits of certain types.
THE PROGRAMMING EXERCISES
One or more programming exercises are given at the end of each chapter for your benefit and
instruction. Each exercise is chosen to encourage you to exercise your new knowledge upon
completing the chapter at hand, and should be completed before continuing on to the next chapter. If
you do the exercises, you will gain experience in writing Ada, rather than simply reading Ada, and
your overall comprehension will be greatly improved. You will gain much knowledge simply by
understanding the compiler error messages you receive during compilation. You can be assured that
you will get compiler error messages. Some of the exercises even tell you to try something to see if
you get an error message.
Answers to all of the programming exercises are given in the a95ans1.zip and a95ans2.zip files
available for download. The file naming format is CHnn_ml.ADA, with "nn" referring to the chapter
number, "m" referring to the specific exercise from that chapter, and the "l" indicating that more than
one answer is given. The answer to programming exercise number 1 from chapter 4 would be in the
file named CH04_1.ADA, and if there were two answers they would be in the files named CH04_1A.
ADA and CH04_1B.ADA and each would have it own results in comments. It would be to your
advantage to attempt the exercises before you look at these files. These files can be compiled and
executed, but due to their nature, they may have compile or runtime errors in them since they are
meant to illustrate some of the problems you can have. The results of compiling and running them
will be reported at the end of the files in comments in much the same way that the example programs
are commented.
Some of the programming exercises have been carefully chosen to illustrate a few additional topics to
augment the material taught in the chapter itself. If you find yourself a bit confused, go directly to the
supplied answer file for clarification and help in solving the problem. You should spend a little time
trying to solve the problem yourself first.
YOUR OWN EXERCISES
Many of the example programs do not produce any output. This has been done on purpose to force
you to write some output statements to see some of the results. You should purposely corrupt some of
the programs, attempting to do strange things to see if you understand the material covered in the
text. One of the initial requirements of this tutorial, as outlined at the outset of this project, was that
all of the example programs would be compilable without error. This makes it difficult to illustrate
some of the compiler checks, so it is up to you to see these errors yourself. If the error is introduced
by you, the compiler error message should be easily decipherable by you. Keep in mind that some of
the answers to the programming exercises will have compile or runtime errors.
RECOMMENDED READING LIST
The following references are recommended to be studied in any order. You should have all of these
references available if you plan to study Ada very seriously.
1. Barnes, John; "Programming In Ada 95", Addison-Wesley. This book defines the language
from the lowest level to a very high level but teaches no software engineering. This is the best
book for the new student to study. It is best described as a reference manual for the Ada 95
programming language.
2. Naiditch, David J.: "Rendezvous with Ada 95", John Wiley & Sons. This book is excellent for
the beginner or for the student with knowledge of Ada 83 desiring to upgrade his knowledge
to Ada 95. The book is very careful to point out which portions of the book refer to Ada 95 by
inserting a little icon in the left margin of the text when the information is pertinent to Ada 95
but not to Ada 83.
3. Ada 95 Reference Manual (ARM); A copy of the ARM probably came with your compiler. A
hypertext version of the ARM is currently available at the following web site; http://www.
adahome.com/rm95 but since any site on the web is subject to change fairly quickly, this URL
may change also. If it is not available at this site, use one of the search engines to locate it.
4. Ada95 Rationale; A copy of the rationale may be included with your compiler, but if not it can
currently be found at the following web site; http://sw-eng.falls-church.va.us/AdaIC.
Note - The web sites listed in references 3 and 4 above contain a wealth of information on Ada 95
and it would be to your advantage to get familiar with their contents.
Advance to Chapter 1
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 1
GETTING STARTED
WHAT IS ADA?
Ada is a relatively new programming language developed by the United States Department of
Defense in an attempt to solve the software muddle as it existed in the mid 1970's. It was felt that the
2000 or so programming languages in use at that time could be replaced in large part by one well
planned language for use in embedded Real-Time systems. Following a major effort on the part of the
DOD, which is well documented in many other places, Ada was developed as a solution to the
software problem.
WHAT IS ADA 95?
Ada 95 is an ISO update to the Ada programming language to incorporate the latest knowledge of
software development into the language. The 1983 version of the language has been called Ada for
years. Since the newer version is meant to eventually supersede, and displace the original version, the
newer version is also referred to as Ada. It is therefore up to the reader to determine which version of
Ada the author is discussing by the context or by a direct statement of intent. To make it as simple as
possible, this tutorial will use the terms Ada and Ada 95 interchangeably when discussing the newer
version of the language, and all references to the original language will be made with Ada 83. Any
place where they are being compared, the text will use the full name, Ada 95, to alleviate any
possible confusion.
Ada is a very well planned and precisely defined language that can be used throughout a wide area of
software applications. The language has existed long enough that a reasonable number of capable
compilers exist for use on mainframe computers, as well as minicomputers, and even
microcomputers. An Ada compiler has a big job to do which you will see as we progress through our
study of the language. It is therefore not a trivial effort to bring a validated Ada compiler to market.
In spite of this, at least three companies have developed fully validated Ada compilers that run under
MS-DOS and/or Windows on a PC. Although some of these will run on a minimal PC, a relatively
powerful PC is recommended for use with any Ada compiler due to the time required for
compilation.
The Ada programming language was designed in such a way that many of the trivial errors, which we
humans are very capable of generating, are detected and reported at compile time rather than after
execution of the program is begun. It is at this point that errors are most easily repaired since a good
compiler can give the programmer a very good hint at just what the error is.
This chapter will give you some definitions so we can begin discussing the use of Ada in chapter 2.
The definitions will be very broad in nature because they are used in many places in an Ada program,
but they are extremely important.
WHAT IS AN IDENTIFIER?
An 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.
1. An identifier must start with a letter of the alphabet.
2. Following the initial letter, the identifier can be made up of as many letters, numbers, and
underlines as desired provided that the underlines occur only singly, and an underline is not
the last character.
3. Case of letters is not significant.
4. There is no limit to the length of an identifier but each identifier must fit on one line of text
and the writer of the compiler may impose a line length limit. The minimum line length must
be at least 200 characters.
5. No blanks or special characters can be used as part of an identifier.
With these rules in mind, lets make up a few good identifiers and a few invalid identifiers.
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 valid
12_times_each -- Can't start with a number
This__is__neat -- Multiple underlines illegal
This is neat -- blanks illegal
Ada_"tutorial" -- special characters illegal
By this time you should get the idea of what a valid Ada identifier is. It may seem like a lot of effort
to define just what an identifier is, but you will be very busy naming everything you use in Ada, so
you must know how to name things before you can do anything meaningful with the language.
IDENTIFIER SELECTION
In addition to an identifier being correct, it should also be usable and meaningful. As an example,
consider the following list of valid identifiers and see which convey to you some idea of what they
refer to.
Time_Of_Day
Final_Score
Get_the_Present_Temperature
X12
Ztx
t
Ada 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.
WHAT ARE RESERVED WORDS?
Ada 95 uses 69 identifiers which are called reserved words. Note that Ada 83, by contrast, only had
63 reserved words. Reserved words 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 reserved words 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 reserved words simplifies that job. The reserved words also make the final program much easier to
read and understand.
Don't worry about the reserved words at this point. It was necessary to mention that they do exist and
constitute an additional limitation to the naming of identifiers which we discussed in the previous
section. It might be a good idea to spend a few minutes looking through the list in section 2.9 of the
Ada 95 Reference Manual (ARM). Note that all reserved words will be listed in boldface type when
used in the text portion of this tutorial.
CASE CONVENTIONS USED IN THIS TUTORIAL
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
identifier is.
In order to write the example programs in a standard format, the author did a search of Ada programs
to see if a standard exists which would dictate which case should be used for alphabetic characters.
The search was conducted by studying the code in the books mentioned in the Introduction to this
tutorial and about 12 other books. No conformance to any standard was found, so the following will
be adopted for all of the sample programs in this tutorial. Since you are just beginning to study Ada,
you may not understand what each of the categories are. After you complete a few of the lessons, you
can return here to review the alphabetic case rules listed for the example programs.
reserved words - All reserved words will be written in lower case. This is the only consistency found
in the search of the Ada programs.
Variables - All variables will be written with the initial letter of each word capitalized, and all others
in lower case.
TYPES - All types will be written in all capital letters.
CONSTANTS - All constants will be written in all capital letters.
ENUM VALUES - All enumerated values will be written in all capital letters.
ATTRIBUTES - All attributes will be written in all capital letters.
Procedure Names - All procedure names will be written with the initial letter of each word capitalized
and all others in lower case.
Function Names - Same as procedure names.
Package Names - Same as procedure names.
Library Names - Same as procedure names.
Note that all program identifiers will be listed in boldface type when they are referred to in the text
portion of this tutorial.
WHAT ABOUT PROGRAMMING STYLE?
Programming style can go a long way to aiding in the understanding of a completed program and
much discussion throughout this tutorial will be given to style. You have the freedom to add
indentation and blank lines to your program in your own way to improve readability and at the same
time make the program look like your own work. In the early lessons, however, it would be to your
advantage to follow the style given in the example programs and adopt it as your own. As you gain
experience, you will develop your own style of Ada source code formatting.
PRELIMINARY DEFINITIONS
Several topics, which are unique to Ada, are used in many places throughout the language. Since a
full definition of these will be impossible until we cover some of the earlier topics, we must delay the
full definition until later. On the other hand, the use of them becomes necessary fairly soon, so we
will give a brief definition of these now, and a complete definition later. If you don't fully understand
these early definitions, don't worry about it, because we will return for a fuller definition later.
Exceptions - When most languages find a fatal runtime error, they simply abort the program. This is
unacceptable for a real time language because it must continue running, correcting the error if
possible. An exception is an exceptional, or error, condition that arises during execution of the
program. An Ada program, if it is properly written, has the ability to define what to do for each of
these error conditions, and continue operation.
Renaming - Ada gives you, the programmer, the ability to assign a new name to various entities in a
program for your own convenience. Ada permits the renaming of objects, exceptions, task entries,
and subprograms. It is simply an alias which can be used to refer to the entity which is renamed.
Overloading - Ada allows you to use the same name for several different items. The system is smart
enough to know which entity you are referring to, when you use the overloaded name, by the
immediate context of its use. For example, if I say, "Jack used a jack to change the flat tire.", you
understand that there are two uses of the word "Jack", and you understand what each means by the
way it is used in the statement. Ada can also use the same name to refer to different things, and
intelligently know what the various uses mean. We will revisit this topic later in this tutorial.
Advance to Chapter 2
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 2
PROGRAM STRUCTURE
OUR FIRST ADA PROGRAM
Example program ------> e_c02_p1.ada
The best way to begin our study of Ada is to look at a real Ada program, so by whatever means you
have at your disposal, display the Ada program named e_c02_p1.ada on your monitor.
You are looking at the simplest Ada program that it is possible to write. Even though it is the
simplest, it is a complete executable program that you will be able to compile and execute after we
discuss a few of the entities that appear in it.
The word procedure in line 1 is the first of the reserved words we will look at. Until we reach a
much later point in this tutorial, we will simply say that a procedure is a program. In this case,
therefore, the program is defined by the reserved word procedure followed by the program name and
another reserved word is. Following the reserved word is, we have the actual program extending to
line 5. The reserved word is therefore, is saying that the program (or procedure) which is named
Trivial is defined as everything that follows.
The program name, in this case Trivial, must be selected by following all of rules given in chapter 1
for naming an identifier. In addition to those rules, it cannot be one of the 69 reserved words.
WHERE IS THE ACTUAL PROGRAM?
There are two portions of any Ada program, a declarative 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. In this particular case, there is nothing in the declaration part, and the executable part
consists of line 4. (We will return to line 4 shortly.) 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.
The actual program, the executable part, is line 4 since that is the only statement between the begin
and end reserved words. In this case we wanted to study the simplest Ada program possible so we
wanted the program to do nothing. We explicitly tell the Ada compiler to do nothing which is the
definition of our fifth reserved word null in line 4. Ada demands that you explicitly tell it that you
really mean to do nothing rather than simply leaving the executable part of the program blank which
would be acceptable in most other languages.
WHAT IS A STATEMENT TERMINATOR?
Line 4 ends with a semicolon which is a statement terminator in Ada. It tells the compiler that this
particular statement is complete at this point. Later in this chapter you will see why the statement
terminator is required. 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.
Lines 10 and 12 are Ada comments and will be ignored by the compiler. A complete definition of
Ada comments will be given at the end of this chapter. All example programs in this tutorial will give
you the results of execution at the end of the program as illustrated here.
It should be clear that a complete Ada program uses at least the four reserved words to define the
beginning and end of each of the fields and they must come in the given order. Of course many other
things can be included in the declarative part and the executable part which we will see during the
remainder of this tutorial.
The program outline can be given as follows;
procedure <program name> is
<declarative part>
begin
<executable part>
end <optional repeat of program name>;
At this point it would be wise for you to compile and run this program to see that it truly does obey
all the rules of the Ada compiler. Unfortunately, it doesn't do anything, so running it will give you no
results. Even though this program does nothing, any good Ada compiler will allow you to compile,
link, load, execute, and properly terminate execution of this program.
NOW FOR A PROGRAM THAT DOES SOMETHING
Example program ------> e_c02_p1.ada
Observe the program named e_c02_p1.ada for an example program that really does something.
Ignore the first two lines for the time being, they are needed to tell the system how to output data to
the monitor. We will study them in a later lesson. Considering only lines 4 through 10, you will see
exactly the same structure used in the last program with a different program name and something new
in line 8.
Line 8 is a call to a procedure named Put which is supplied with your Ada compiler as a convenience
for you. It is very precisely defined so that you can use it to display text on your monitor. The string
of characters contained within the parentheses and quotation marks will be displayed on your monitor
when you compile and execute this program. The procedure named Put is actually a part of a library
named Ada.Text_IO which is why the first two lines are included in this program. They tell the
system where to find the procedure named Put, and how to use it. (We will discuss these two lines in
great detail later in this tutorial.)
Once again, the executable statement in line 8 has a semicolon at the end as the statement terminator
in the same manner that the reserved word null was followed by a semicolon in the last program.
Compile and execute this program and observe that the phrase in line 8 is displayed on the monitor
each time you execute the program.
A LITTLE MORE OUTPUT
Example program ------> e_c02_p1.ada
Examine the program named e_c02_p1.ada, and you will see a few differences from the last two
example programs. Observe that the program name is not repeated in the last line of the program.
This is optional as we stated earlier, but it is a good practice to include the name.
The second, and most obvious difference, is the fact that there are several executable statements in
this program. The executable statements, as with most other procedural programming languages, are
executed in sequential order from top to bottom. The lines with calls to the procedure Put are
executed just like the last program except a new operation becomes apparent here because we have
multiple Put calls. The Put call does not cause the cursor to return to the beginning of a line
following output of the line of text. The cursor simply stays where it ends up at, resulting in lines 8
and 9 being output on the same line of the monitor. Another new procedure, at least new to us, is
named New_Line and this procedure causes the cursor to be returned to the beginning of the next line
of the monitor. In fact, when using the New_Line procedure, you can even include an optional
number within parentheses following the procedure name, and the cursor will be moved down that
number of lines. This is illustrated in lines 12 and 14, and if the optional number is omitted, a value
of 1 is assumed.
Note carefully that the procedure names used here, Put and New_Line, meet all of the requirements
for naming an identifier which we studied in chapter 1. These names were selected by the Ada
language definition committee.
Lines 16 and 17 introduce another useful procedure, named Put_Line, which causes an automatic
"carriage return" to be output after the string within the parentheses is output. You will find this to be
very useful, and should begin using it immediately. The blank line in line 15 is ignored by the Ada
compiler. More will be said about the use of white space in the next two example programs.
HOW DID WE NAME THE IDENTIFIERS?
We have the option of naming our program any name we wish as long as we obey all of the rules of
naming an identifier listed in Chapter 1 of this tutorial. We have a restriction on the program name
because of the way Ada compiles and links a program. In order to meet all of the requirements as
specified in the Ada 95 Reference Manual (ARM), a compiler must generate some form of
intermediate files containing object and type information. Any particular Ada compiler may use a
naming convention for the intermediate files based on the program name we supply, or the file name
we supply, so in order to avoid confusion, the same name will be used in both places throughout most
of this tutorial. Therefore the name of the procedure, MoreOut in this case, will be the same as the
name of the file, e_c02_p1.ada in this case. Note that your particular Ada compiler may not have this
limitation.
With the above information, you should be able to figure out what this program will do. Compile and
run it to see if you are correct in your analysis. This is a great place for you to begin programming by
using these three subprograms to output some text in a neat format.
CONSIDER THE STYLE OF ADA PROGRAMMING
Example program ------> e_c02_p1.ada
Observe the program named e_c02_p1.ada and you will see a familiar form, and a very clear and easy
to understand Ada program. You should have no problem at all understanding what this program
does. You should observe that 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.
Once again, don't worry about the first two lines, we will discuss them later.
Example program ------> e_c02_p1.ada
After compiling and executing e_c02_p1.ada, observe the next example program named e_c02_p1.
ada for an example of terrible formatting style. See if you can figure out what this program does, then
decide if you would like to debug it if a problem should surface. Finally, compile and execute this
program and you will find that it actually does compile and execute, doing exactly what the last
program did.
These two programs were intended to give you an idea of the amount of freedom you have in
formatting style when writing an Ada program and the amount of information the style can add to a
program.
COMMENTS IN AN ADA PROGRAM
Example program ------> e_c02_p1.ada
Examine the program named e_c02_p1.ada for an example of how comments are added to an Ada
program. 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 program gives many illustrations of where comments can go and
it will be left to your study. Note that line 16 is not an executable statement since it is commented
out. As with all programs in this tutorial, this one is executable, so you should compile and execute it
at this time.
One other point must be made. This example program is not meant to be an illustration of good
commenting style, only an indication of where comments can go. It is actually a very choppy looking
program, and is not at all clear.
PROGRAMMING EXERCISES
1. Write a program to display your name on the monitor.(Solution)
2. Write a program to display your name, address, and phone number on different lines of the
monitor.(Solution)
Advance to Chapter 3
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 3
THE INTEGER TYPE VARIABLE
OUR FIRST INTEGER VARIABLE
Example program ------> e_c03_p1.ada
Examine the program named e_c03_p1.ada for our first example program with a variable. Some
programming languages do not require you to predefine a variable before you use it. Instead, you
simply begin using it and the system has some mechanism by which it creates the variable and makes
it ready for your use. Ada requires you to specifically define every variable before you use it. You
must give the variable a name, which is any valid identifier, and you tell the compiler how you plan
to use the variable by assigning a type to the variable.
WHAT IS A 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.
AN UNBROKEN RULE OF ADA, NEVER BROKEN
Ada requires that anything you use must have been previously defined. This includes variables as
well as constants, procedures, functions, and all other entities. At least one language, Pascal, makes
this claim but breaks it in a few instances. Ada never breaks this rule.
THE with AND use STATEMENTS
The with and use statements, in lines 2 and 3 of this program, contain Ada.Integer_Text_IO in
addition to Ada.Text_IO and a few comments are in order at this point, event though they will be
completely defined later in this tutorial. The term Ada.Text_IO refers to an Ada package of the same
name that provides us with the ability to output text to the monitor including characters, strings,
carriage returns, and various other entities so that we can generate some formatted text output to the
monitor. It also gives us the ability to input text from the keyboard, and it provides some file input/
output capabilities.
The term Ada.Integer_Text_IO refers to another Ada package of that name which gives us the
ability to output INTEGER type variables to the monitor in a well formatted way since it gives us
control over how many columns are used, and what base to use for the numbering system, such as
binary, decimal, hexadecimal, and other options. The with statement, because it mentions both of
these library packages, provides us with the ability to output both text and numeric values to the
monitor, because it makes a copy of both of these libraries available for use by our program. The use
statement, because it mentions both of these packages, makes it very easy to "use" these packages
within our program. Without the use statement, we would have to qualify every input or output
statement, which makes for very ugly code, but which some programers find preferable for various
reasons.
We will cover all of this in great detail later in this tutorial, but it would be best for you to simply
include the appropriate packages in a with statement and a use statement for your initial
programming efforts and pick up the additional knowledge later. There is only so much you can
absorb and understand at one time, and the author of this tutorial desires to simplify your study of
Ada as much as possible by deferring some topics until later.
HOW DO WE DECLARE A VARIABLE?
Back to the program named e_c03_p1.ada. To understand the definition of a variable, examine the
program at hand and specifically line 7. This declares a variable, which we will call Index, to be of
type INTEGER, therefore defining an allowable range of values which can be assigned to it. Most
16 bit computers allow an INTEGER type variable to cover a range of -32,768 to 32,767. The
corresponding range for 32 bit computers is from -2,147,483,648 to 2,147,483,647 but the definition
of Ada allows for flexibility in both of these ranges. We will see a way to determine exactly what the
limits are for your compiler later in this chapter.
The type also defines a number of operations which can be performed on the variable. More will be
said of that later. The type INTEGER is used to declare a scalar variable, which is a variable that can
contain a single value.
The word INTEGER is not a reserved word but a predefined word which we can redefine if we so
choose. We will not be doing that for a long time since it could cause untold problems in a program.
You should know that it is possible for you to redefine this word, and many other similarly
predefined words, to mean something entirely different from their predefined meanings.
In the last chapter, we said that the declarative part of the program goes between the reserved words
is and begin, and you will notice that we did indeed declare the variable named Index in that area of
the program. The end result of line 7 is that we have a variable named Index which is of type
INTEGER. As yet however, it does not contain a useful value.
HOW DO WE USE THE VARIABLE IN A PROGRAM?
In the last chapter we also said that the executable statements went between the begin and the end
reserved words and you will see that we have some executable statements within that range, in lines
11 through 18 of this program. In line 11 we assign the value of 23 to the variable Index which is
valid to do because 23 is within the range of allowable values that can be assigned to an INTEGER
type variable.
THE ASSIGNMENT OPERATOR
The combination := can be read as "gets the value of". Line 11 can then be read as, "Index gets the
value of 23." The equal sign alone is reserved for another use. Actually, if we say that Index = 23, 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.
We have succeeded in assigning a value of 23 to the variable named Index and can use it in many
different ways in an Ada program, but we will illustrate only very simple uses at this point.
Line 12 instructs the system to output a line of text to the monitor and leave the cursor at the end of
the line. In line 13, we use the predefined procedure named Put to tell the system to display the value
of Index, which has the value of 23, on the monitor. The system will right justify the output in a field
width that depends on the size of an INTEGER on your system, because of its definition. This Put is
part of the Ada.Integer_Text_IO package we declared earlier. The Put in line 12 is from Ada.
Text_IO. Later in this tutorial you will understand which Put is from which package and why. Line
14 returns the cursor to the beginning of the next line. The New_Line procedure is from the Ada.
Text_IO package.
OUR FIRST ARITHMETIC
Line 15 contains our first arithmetic statement and it will do exactly what it appears to do, "Index
gets the value of Index with 12 added to it." The variable Index should now have a stored value of 23
+ 12, or 35, which we verify by telling the system to display the new value of Index. The numeral 8
in line 17 tells the system to display the value right justified in a field 8 columns wide. We will
discuss the Put procedure in detail later in this tutorial. If you remember that the statements are
executed sequentially, you should have no difficulty following this program.
Compile and execute this program being careful to observe that the two values of Index are displayed
in fields of different widths.
LET'S USE LOTS OF INTEGERS NOW
Example program ------> e_c03_p2.ada
Examine the program named e_c03_p2.ada and you will see an example of using many variables of
type INTEGER in a program. Lines 7 and 8 illustrate that you can define one or more variables on a
single line. All four variables are of type INTEGER and can be assigned values within the range of
integer variables as defined for your particular compiler. Each of these variables have no value
associated with them since they were created without an initial value. The executable part of the
program can assign a value to each of them. The variable named Cat is also an INTEGER type
variable but after being created, it is assigned an initial value of 12. Likewise Dog is created and
initialized to a value of -5. It should not come as a surprise to you that the three variables in line 11
are created, and each is assigned an initial value of 1000.
According to the Ada definition, line 11 is merely a shorthand for three different lines with a single
variable declaration on each line as far as the Ada compiler is concerned. The same is true of line 8.
This is a very subtle point, and has no consequence on the program at hand, but will make a
difference later when we commence the study of arrays.
NOW TO EXERCISE SOME OF THOSE VARIABLES
Examining the executable part of the program we find the four arithmetic operations in lines 15
through 18 which should be self explanatory. Note that integer division in line 18 results in
truncation, not rounding. The four values are displayed on the monitor in lines 19 through 22 in a
format utilizing several statements per line which is simply a matter of style.
Continuing with lines 24 and 25, we have examples of more complex mathematical calculations
which should be clear to you. The order of precedence of mathematical operators is given in detail in
section 4.5 of the Ada 95 Reference Manual (ARM). The order of precedence is similar to other
languages and follows common sense. A discussion of the order of precedence will be given at the
end of the next chapter of this tutorial.
Lines 26 and 27 illustrate use of the mod and rem operators, each of which return the remainder
which would be obtained following an integer divide operation. They differ only in the sign when
negative numbers are involved and since negative numbers are rare when using these operators, little
will be said except to give a brief statement of the differences.
mod - gets the sign of the second operator.
rem - gets the sign of the first operator.
TWO MORE OPERATIONS
After displaying the results, four more values are calculated, the first being the absolute value of the
variable Dog in line 33. This is not a function, it is an operation defined with the reserved word abs.
The fact that it is an operator will be very significant when we come to the portion of this tutorial that
deals with overloading operators. The abs operator returns the absolute value of the variable given to
it as a parameter.
The operation in line 34 is an illustration of exponentiation of integer numbers. Since Cat has the
value of 12, this line says that Index_2 gets the value of 12 raised to the 3rd power. The only rules
for using exponentiation with integer values are, the exponent must be an integer type value, and it
cannot be negative. Note that a zero value for an exponent is legal. Line 35 is a combination of
several of the previous operations, and line 36 is an illustration of unary negation.
Be sure to compile and execute this program and study the results.
HOW DO WE DECLARE A CONSTANT?
Example program ------> e_c03_p3.ada
Examine e_c03_p3.ada for an example of a program with some INTEGER type variables and some
INTEGER type constants declared and used in it. Lines 7 and 8 should be familiar to you now, but
when we get to lines 10 through 13 we have a few new things to observe. DOZEN and GROSS are
INTEGER type constants because of the reserved word constant in their declaration. The only
difference between a constant and a variable is that a constant cannot be changed during execution of
the program and in the present example, it would probably be silly to redefine how many elements
are in a dozen. This is one of the things Ada can do to help you if you analyze your program right,
because if you ever tried to change the value of DOZEN during program execution, Ada would give
you an error message and you would eliminate one bug immediately.
Notice that GROSS is defined in terms of DOZEN since the constant DOZEN is available when
GROSS is initialized. Likewise, the constant TWO is defined in terms of BIG_NO in the next two
lines. It should be obvious that a constant must have an initialization value assigned to it at the point
of declaration.
TWO MORE DEFINITIONS
Lines 12 and 13 contain underlines in the numeric values that the Ada compiler will simply ignore.
You can put them in wherever you please to make the numeric literals more readable for you, but you
cannot put more than one underline between each digit. The poor choice of underline locations in this
example do not add to the ease of reading the numbers but illustrate the places where they can be
located. The word INTEGER has been omitted from lines 12 and 13, which makes the type of these
constants slightly different from the other two but we will have to learn a bit more before we can
understand or appreciate the value of doing this.
Lines 17 through 25, in the executable part of the program, should be easy for you to understand on
your own, so they will be left to your study.
DECLARING LITERAL CONSTANTS
Lines 27 through 29 give examples of declaration of literal values using the exponential notation. The
exponent can be indicated with either case of "E" and the number following it must be positive for an
INTEGER literal. Lines 30 through 33 give examples of the use of a base other than 10. The radix of
the number is given prior to the first "#" sign, and can be any value from 2 through 16, the radix itself
being given in decimal notation. The value is given between "#" signs and can be followed by an
optional exponent, the exponent being given in the defined base. If no radix is given, base 10 is
assumed as in lines 27 through 29 of this example program.
The executable part of this program should be very clear to you. Be sure to compile and execute it.
TYPES AND SUBTYPES
Example program ------> e_c03_p4.ada
Examine the program e_c03_p4.ada for your first look at a user defined type. We mentioned earlier
that the range of a variable of type INTEGER could be different on different computers or with
different compilers on the same computer. Ada gives us the ability to define our own type in such a
way that it will be identical on every computer and with every Ada compiler.
A DIFFERENT TYPE IS INCOMPATIBLE
Line 7 defines a new integer type which will cover the range of -10,000 to 20,000 because of the use
of the reserved word range to limit the available range of values that can be assigned to a variable of
this type. Notice carefully that we called this an integer type, not a type INTEGER. Since it is an
integer type, it has all of the properties defined earlier in this chapter but a variable of this type can
only be assigned a value from -10,000 to 20,000. The actual range is given by specifying the lower
and upper limits separated by two decimal points.
We have actually defined an entirely new type, and since Ada does very strong type checking, it will
not allow you to assign a value directly from a variable of type INTEGER to a variable of our new
type, or vice versa. The variable My_Int is defined with the new type in line 8. We will return to
consideration of this variable later.
The package declaration in line 10 is completely new to us, so a few comments on it are in order. The
complete definition of line 10 in Ada terminology will be given first, including a lot of new words
which will mean very little to you until you gain some additional knowledge which will be given later
in this tutorial. Line 10 is an instantiation of the generic package named Ada.Text_IO.Integer_IO
with the type MY_INT_TYPE to provide the ability to output values of MY_INT_TYPE to the
monitor or to files, or to input from the keyboard or from files. Saying the same thing in a more
useful way, if you want to input or output values in some integral class type other than the predefined
type INTEGER, you must include the line as shown with the desired type in the parentheses. Line 11
is included to make the new package easy to use. You will notice that it tells the system to use the
package named in line 10 immediately following the keyword package.
As stated earlier, in an attempt to simplify the Ada learning curve, some topics will be deferred until
later to give you a chance to absorb a few topics at a time, so don't spend any time trying to
understand the previous paragraph at this time.
A NEW SUBTYPE IS COMPATIBLE WITH ITS PARENT
In line 13, we define a new subtype which is of the parent type INTEGER except that it covers a
limited range, then we declare a variable named Thing of the new subtype in line 14. Any attempt to
assign a value to the variable Thing which is outside of its assigned range, will result in an error. If
you were running a small company with 23 employees and you assigned them each a unique number
from 1 to 23, you would be interested if the payroll program tried to generate a paycheck for
employee numbered 36, for example, because there would definitely be something wrong. A limited
subrange could save you a lot of money in a case such as that.
ASSIGNMENT MUST BE TYPE COMPATIBLE
Anytime a value is assigned to a variable, the type assigned to it must be of its declared type or a
compiler error will be generated. This will always be true in Ada and is one of the most important
concepts behind its design. This is to prevent us from making the silly little mistakes which we
humans are so good at making.
The variable Count is defined as an INTEGER type variable and Stuff is defined as a limited range
INTEGER also. Remember that Thing is declared to be a subtype of INTEGER, so it is also a
limited range INTEGER type variable. Because of the way these three variables are defined, they
can be freely assigned to each other as long as the values assigned are within their respective ranges.
This is because they are all of their parent type of INTEGER, and various assignments among them
are illustrated in lines 25 through 27.
If we tried to assign the value of Thing to My_Int, the computer would give a compile error because
they are of different types, but an explicit type conversion can be used to do the assignment as
illustrated in line 30. By including the variable to convert to a new type in parentheses and preceding
the parentheses with the desired type name, the system will convert the type as illustrated. The
addition to Thing is completed and the entire expression inside of the parentheses is changed in type,
by the explicit type conversion, to the desired type of the left side of the assignment statement. Note
that explicit type conversion can only be done from within the same type class, in this case the integer
class.
In line 31, the type is changed to the new type before the value of 17 is added to it, which should lead
you to ask a question about the type of the constant 17.
HOW CAN 17 BE ADDED TO EITHER TYPE?
The constant 17 is of a very special type defined by Ada as type "universal_integer" which can be
combined with any of the integer types without specific conversion. This term will be used in many
ways in future lessons. The type universal_integer is compatible with all integer types and has a range
with no limits. The range is effectively minus infinity to plus infinity. The type universal_integer is
used for all literal values, but it is not available for your use in declaring a variable.
NOW FOR A SUBRANGE ERROR
Lines 30 and 31 are essentially the same because they result in the same answer, and lines 33 and 34
would appear to be the same, but they are not. In line 33, the value of Thing, which is 18, is
converted to type MY_INT_TYPE and 10 is subtracted from it. Both 18 and 8 are within the
specified range of MY_INT_TYPE so there is no error. In line 34 however, the result of the
subtraction has the type of Thing and even the intermediate result is required to be within the range
of 12 to 144. The result of 8 is outside of the required range so a run-time error is signaled in a
special way called raising an exception. We will discuss the exception shortly but first notice that if
we could get past the error, we could change the type of the result to MY_INT_TYPE and
everything would work as desired.
INTERMEDIATE RESULTS OF CALCULATIONS
The ARM allows the limits of intermediate results to be checked against the limits of the parent type
rather than the limits of the subtype. Line 34 therefore, may not give an error indicating that the
intermediate result is out of the allowable range. This will depend on your compiler.
WHAT IS AN EXCEPTION?
We will have a lot to say about exceptions as we progress through this tutorial but a very brief
description is needed at this time. When an Ada program is running, and a potentially disastrous error
is detected, it would be good for you, the programmer, to be able to tell the system what to do with
the error rather than cause a complete termination of the program. Ada gives you that capability
through the use of exception handlers that you write and include in your program. In the above case,
when the program detected the value of 8 as being out of the allowable range of that type of variable,
it would signal your program that the error occurred, and if you did nothing, the Ada system would
terminate the program. The proper Ada terminology for signaling you is called "raising an
exception", and in the case of an out-of-bounds value, the exception named Constraint_Error would
be raised. You could then trap this error and handle it any way you choose to.
There are several different exceptions that the system can raise and you can define your own
exceptions which your program can raise and respond to. We will have a lot more to say about
exceptions later in this tutorial.
A little study on your part should reveal why line 38 also has a run time error that will raise the
exception Constraint_Error, if execution continues to that statement.
ANOTHER LOOK AT THE universal_integer
Before we leave this program we must look at lines 18 and 19 where we first define START and
STOP as constants with no type indication. These constants are therefore of type "universal_integer"
and can be used in the range definitions of the next two lines even though the two lines are of
different parent types. If we had included the word INTEGER in the definitions in lines 18 and 19,
they could not be used to define the limits of Example2 because it is of a different type. The example
program named e_c03_p3.ada had examples of INTEGER constants (DOZEN and GROSS) and
universal_integer type constants (BIG_NO and TWO). The type universal_integer is actually a
hidden type that is type compatible with all integer types.
It should also be observed that the limits of the range in these definitions are based on previously
defined entities and can be of arbitrary complexity as long as they evaluate to the right types. The
limits must also be within the range of the parent type or a compile error will result.
HOW TO DEFINE TYPES AND SUBTYPES
To declare types or subtypes, here are two simple formulas which can be followed.
type <type_name> is <type_definition>;
subtype <subtype_name> is <subtype_definition>;
Note that each declaration starts with a reserved word and includes the reserved word is between the
name and the definition. It should be pointed out that Ada permits new types, subtypes, and variable
declarations to be done in any order as long as everything is defined before it is used.
Compile and execute e_c03_p4.ada and observe the exception error as reported by your runtime
system. The exception may be raised at line 34, depending on your compiler, where the value of 8 is
outside of the allowable range of 12 through 144. The error message will vary with different
compilers.
Much more will be said about types and subtypes in chapter 7 of this tutorial.
WHAT ARE ATTRIBUTES?
Example program ------> e_c03_p5.ada
Ada has a rather large list of attributes available for you as a programming aid. For an example of a
program that contains, and therefore illustrates the use of attributes, examine the program named
e_c03_p5.ada.
A new type is defined in line 7 with a limited range to illustrate that attributes are available even for
user defined types.
Two additional predefined integer types are introduced in lines 10 and 11, the NATURAL and
POSITIVE types. The POSITIVE type includes all integers greater than or equal to 1, and the
NATURAL type includes all integers greater than or equal to 0. Both of these are available with your
Ada compiler, and both are subtypes of INTEGER, so all variables of these three types can be freely
mixed with no type errors.
Attributes are used to gain access to various limits within the program. For example, it may be
necessary to know the upper limit of a variable of some type because it is of some strange subtype.
An attribute can be used to find this limit, specifically the attribute LAST as illustrated in line 28 of
this program. By combining the type name and the attribute in question with a "tick" or apostrophe,
the upper limit of the range of the type is returned. The attribute FIRST is used to find the lowest
value allowed by the subrange. The attribute SIZE gives the storage size of the type in bits of
memory. Other attributes are available for integer types. A complete list of available attributes is
given in Annex K of the ARM. You should spend a few minutes reviewing the list at this time even
though you will understand little of what is presented there. In the near future, you will understand
most of the material included there.
TYPE CONVERSIONS
Note the type conversions in lines 63, 67, 69, and 72. The values could be output directly with the
BUG_RANGE type by instantiating a copy of Ada.Text_IO.Integer_IO in a manner similar to that
done in the example program named e_c03_p4.ada, but we choose to use type conversion rather than
instantiating a new copy of the I/O package. Because NATURAL and POSITIVE are subtypes of
INTEGER, they use the copy of the I/O package pre-instantiated for the INTEGER type by the Ada
system.
Compile and run this program and you will get a listing of the number of bits required by your
compiler to store each of the four types along with the range covered by each of the four types.
PROGRAMMING EXERCISES
1. Write a program with some types containing some constrained limits and see what errors
occur when you exceed their limits.(Solution)
2. Try to assign some wrong type of data to some variables and try to mix up some types in
arithmetic statements. Study the compiler error messages.(Solution)
3. Modify e_c03_p5.ada to output the BUG_RANGE attributes directly by instantiating a new
copy of the generic package Ada.Text_IO.Integer_IO. Don't spend too much time on this
exercise before you look at the answer. It contains a new construct, at least new to you.
(Solution)
Advance to Chapter 4
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 4
LOGICAL COMPARES AND PRECEDENCE
WHAT IS A BOOLEAN VARIABLE?
Example program ------> e_c04_p1.ada
Examine the program named e_c04_p1.ada for an example of logical compares being used in a very
trivial way. We declare and initialize three INTEGER type variables for use later then declare two
variables of type BOOLEAN in lines 14 and 15, with the first one being initialized to TRUE. A
BOOLEAN type variable has a very limited range of values which can be assigned to it, namely
TRUE or FALSE, and there are no mathematical operations available for use on variables of type
BOOLEAN.
Much more will be said about the BOOLEAN type variable later in this tutorial, but we need a basic
understanding of a boolean variable in order to study program control in the next chapter.
Lines 19 and 23 illustrate how you can assign a value of either TRUE or FALSE to a BOOLEAN
variable. These illustrate literal assignment in much the same way that a literal value can be assigned
to an INTEGER type variable. Because we wish to display BOOLEAN values, we instantiate a copy
of the generic package Enumeration_IO for the BOOLEAN type. Once again, we will study the
details of this later in the tutorial. This package makes it possible to output BOOLEAN values in
lines 21 and 25.
BOOLEAN ASSIGNMENT STATEMENTS
Lines 28 through 30 are a bit more interesting because they illustrate assigning a calculated
BOOLEAN value to a BOOLEAN variable. In line 28, the value of the INTEGER variable One
has the literal 1 added to it and the total is compared to the value contained in the variable Two. The
expression reduces to the expression 1 + 1 = 2, and since 1 + 1 is equal to 2, the expression evaluates
to TRUE which is assigned to the BOOLEAN variable Is_It. The various steps in evaluation are
illustrated below for the student with little or no experience using BOOLEAN expressions in some
other programming language.
Is_It := (One + 1) = Two;
Is_It := (1 + 1) = 2;
Is_It := 2 = 2;
Is_It := TRUE;
The single equal sign is the BOOLEAN operator for equality, and if the two expressions being
evaluated are of the same value, a value of TRUE will result. If they are not of the same value, the
BOOLEAN result will be FALSE.
ARE THERE OTHER BOOLEAN OPERATORS?
There are six BOOLEAN operators, and all six will be illustrated in the next example program. Line
29 in the present program illustrates use of one of the others, the inequality operator. This statement
says, if One is not equal to Two then assign the value of TRUE to the BOOLEAN variable Is_It,
otherwise assign a value of FALSE to the BOOLEAN variable Is_It. Note that regardless of the
result, a value will be assigned to the BOOLEAN variable. Finally, the "greater than or equal"
operator is illustrated in use in line 30.
This is a rather silly program since none of the results are used, but it is meant to illustrate just what a
BOOLEAN variable is and how to use it. Compile and run this program before continuing on to the
next example program.
ADDITIONAL TOPICS ON BOOLEAN EVALUATION
Example program ------> e_c04_p2.ada
Examine the program named e_c04_p2.ada and you will see all six BOOLEAN operators in use. We
define two INTEGER and three BOOLEAN type variables in the declaration part of the program,
and begin the executable part with some compare examples in lines 14 and 15. It should be clear that
in line 15, Index can not be less than itself, so the result is FALSE.
Lines 18 through 23 illustrate the use of all six BOOLEAN operators which are available in Ada and
you should have no trouble understanding this list.
THE BOOLEAN and OPERATOR
Lines 26 through 29 illustrate composite BOOLEAN operators using the reserved words and, or,
not, and xor. The statement in line 26 says that
if Index = 12
and if Count = 12
and if Truth currently has the value TRUE
and if TRUE (which is always TRUE)
then assign TRUE to Question
otherwise assign FALSE to Question.
Using the freeform available in an Ada program to write the previous sentence, can help in
understanding the sentence. The point to be illustrated is that you can combine as many BOOLEAN
expressions as you desire to do a particular job.
THE BOOLEAN or OPERATOR
Using the expanded freeform available in Ada, the statement in line 27 says that
if Index is not equal to 12
or if FALSE (which is always FALSE)
or if Count is greater than 3
or if Truth currently has the value of TRUE
then assign TRUE to Question
otherwise assign FALSE to Question.
THE BOOLEAN not OPERATOR
The expression in line 28 illustrates the use of these two operators combined in a slightly more
complex way using parentheses to properly group the expressions. A new operator appears here, the
not which simply says to reverse the meaning of the BOOLEAN operator which it precedes.
THE BOOLEAN xor OPERATOR
Line 29 illustrates the use of the "exclusive or" operator which says that the result will be TRUE if
one and only one of the operands are TRUE, and FALSE if both operands are TRUE, or both
operands are FALSE. A good illustration of this operation would be a hall light with a switch at both
ends of the hall. If one of the switches is up, the light is on, but if both are up or both are down, the
light is off.
FULL EVALUATION OF BOOLEAN EXPRESSIONS
The way the expressions are written in lines 26 through 29, all of the expressions will be evaluated
when the statement is executed. If, in the case of line 26, the value of Index is not 12, then the final
result will be FALSE no matter what the rest of the expressions are and it would be a waste of time
to evaluate them. Ada will continue blindly across the entire line evaluating all of the expressions and
wasting time since it should know the final result based on the first comparison. There is however, a
way to tell it to stop as soon as it knows the final answer, through use of the short circuit operators.
WHAT ARE "SHORT CIRCUIT OPERATORS"?
If you study line 32, you will see that if Index is equal to Count, we will be dividing the constant 9
by zero in the second part of the expression. By adding the reserved word then to the and operator
we have a short circuit operation, which means as soon as the system knows the final outcome, the
remaining operations are short circuited and not evaluated. In the present example, if Index is equal
to Count, the first term is FALSE and there is no need to continue since the second term is to be
anded with the FALSE resulting in FALSE no matter what the second term is. Division by zero is
avoided in this case because the division is not attempted. In the same manner, line 33 illustrates the
short circuit or operator which is obtained by adding the reserved word else. In this case, if Index is
equal to Count, the result will be TRUE regardless of what the second term is, so the second term is
not evaluated and division by zero is avoided. Line 34 is identical to line 33 but illustrates the use of
parentheses to make the logic a little easier to read.
It should be clear that Ada provides the tools needed to do any boolean operation needed. It is up to
you to learn how to use them.
ORDER OF PRECEDENCE
The order of precedence of operators is given by the following list.
** not abs -- Highest precedence

* / mod rem -- Multiplying operators

+ - -- Unary operators

+ - & -- Binary adding operators

= /= < -- Relational operators
<= > >= -- Relational operators
in not in -- (same precedence)

and or xor -- Logical operators
and then or else -- (same precedence)
The Ada 95 Reference Manual (ARM) has a complete list of the operators and the details of the order
of precedence in section 4.5. If there is any question as to the order of precedence, you should group
expressions together with parentheses since they have the absolute highest precedence. A future
reader of your program will know exactly what your program is doing.
Be sure to compile and execute this program. Note that we have not yet studied the &, the in, and the
not in operators but will soon.
PROGRAMMING EXERCISE
1. Add some output statements to both example programs to see that the results are as predicted.
This will give you experience using the boolean output statement.(Solution)
Advance to Chapter 5
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 5
CONTROL STRUCTURES
THE SIMPLEST LOOP IN ADA
Example program ------> e_c05_p1.ada
We will start with the simplest loop in Ada, the infinite loop which is illustrated in lines 11 through
16 of the program named e_c05_p1.ada. The variable named Index is initialized to the value of 1
prior to entering the loop, and the loop itself is given in lines 11 through 16. The loop begins with the
reserved word loop and ends with the two reserved words end loop. Any number of executable
statements are placed between these two delimiters and the loop is repeated continuously until
something causes the program to jump out of the loop. In this case, the variable Index is incremented
each time through the loop, and when it reaches a value of 5, the exit statement is executed which
causes control to jump out of the loop and begin executing instructions immediately following the
end of the loop. The words exit and when are two more reserved words.
The expression following the exit when reserved words must evaluate to a BOOLEAN result and
when the result is TRUE, the loop is exited, but as long as the result is FALSE, the loop execution
will continue. Note that the exit statement can be placed anywhere in the loop and as many
conditional exits as needed can be placed within the loop.
The statements illustrated in lines 18 through 24 use an alternative form of loop exit which uses the if
statement which we have not yet studied. The if form of exit is such a common form of usage that it
had to be included here as one of the simplest types of loops. Note that the if exit can be placed
anywhere in the loop and as many as needed can be used within the loop. The if statement will be
fully explained later in this chapter.
THE exit STATEMENT
There is a subtle difference between the exit in line 15 and the exit in line 23. The exit in line 15 is
conditional because it is only executed if the condition evaluates to TRUE. The exit in line 23
however, is unconditional since the exit will be taken anytime control reaches the word exit. Keep in
mind that an exit is used only to exit a loop, it is not used for any other construct in Ada.
THE while LOOP
The while loop is identical to the simple loop except for the addition of a test prior to the reserved
word loop. The test is done at the beginning of the loop so it is slightly less general than the simple
loop, but it also requires a BOOLEAN expression as part of the construct. This loop is illustrated in
lines 27 to 31 of the present program. In line 27, "while Count < 5" is called the iteration clause.
THE for LOOP
The loops studied so far in this example program use an indeterminate number of passes since they
calculate their own limits as they progress through the loop. It is important to point out that the
BOOLEAN expressions are evaluated on every pass through the loop. The for loop, however, has its
limits evaluated one time and the number of passes through the loop is completely defined before the
first pass through the loop is begun.
The for loop is illustrated in lines 33 through 36 where the control consists of a few words prior to
the reserved word loop. In this case, the loop control is the variable Index and the range of the
variable is 1 through 4. The loop will be executed four times, each time with a larger number for the
variable Index, since the loop index is always incremented by one. There is no provision for an
increment of any other value in Ada. The reserved words for and in are used in the manner shown for
all for loops. They serve to bracket the loop index, in this case named Index, and the range of the
index, in this case 1 through 4 inclusive.
Because the type INTEGER is so commonly used for the loop index, if the type is not specifically
stated, it will be defaulted to the type INTEGER. This is to make it easier to code the majority of
loops.
Note that the loop parameters do not need to be of type INTEGER, but the loop index and both
range limits must be of the same type. Note also that the value of the loop iterator is not available
after loop termination. The loop iterator ceases to exist completely when the loop is completed.
(There is good reason for this as we will see when we complete our study of the next example
program.)
THE BACKWARDS for LOOP
There is a slight variation to the for loop illustrated in lines 38 through 41, where the loop index is
decremented on each pass through the logic. The word reverse is another reserved word and serves to
indicate the backward counting nature of this loop. Note that the range is expressed in ascending
order, but the actual execution begins at 8 and decrements by 1 on each pass until it reaches 5, where
it quits after the pass through the loop with Count set to 5.
THE EMPTY LOOP
Continuing in the program named e_c05_p1.ada, the loop in lines 43 to 45 is given to illustrate that it
is possible to write a loop that does absolutely nothing. It may seem like a silly thing to do, but there
are cases, when using tasking, that it is necessary to do nothing in a loop. It is of more importance at
this point to illustrate that Ada is so picky, you are not allowed to write an empty loop, but are
required to inform the compiler that you really did mean for the loop to do nothing by including the
reserved word null as a statement within the loop.
Be sure to compile and execute this program and be sure you understand exactly what each loop
does.
SLIGHTLY MORE COMPLEX for STATEMENTS
Example program ------> e_c05_p2.ada
Examine the program named e_c05_p2.ada for a few examples of more complex but much more
versatile for loops. The loops illustrate a few of the flexibilities designed into Ada to allow for more
efficient programming. The first example in lines 21 through 25, has two new concepts, the first
being that the loop variable, Index, is not declared in the definition part of the program, and secondly
the range is defined by a type rather than variable or constant limits.
Considering the first point, the loop index in a for loop does not require explicit declaration, but will
be implicitly declared by the program, used during the duration of the loop, and discarded when the
loop terminates. The final value of the loop index is not available for use after the loop terminates,
and this is true regardless of whether the index is explicitly or implicitly declared. (We will see
shortly that it is always implicitly declared by the Ada system.) The Ada language designers gave
compiler writers freedom on how and when the loop index is incremented rather than dictating what
the final value would be. It is a simple rule to remember that you can not depend on having the final
value of the loop index when you terminate the loop. If you need the final value, you must copy it
into some other variable prior to leaving the loop.
USING A TYPE FOR THE LOOP RANGE
The second point brought out above is the fact that the loop range is given as a type. The type given,
MY_TYPE, has a defined range of 10..13, so it should be no real problem seeing what the loop index
range is. Moreover the implicit loop index, named Index, will also be of type MY_TYPE. Because
we wish to print the value of the loop index during each pass through the loop, we instantiate a copy
of the Integer_IO package for the MY_TYPE type in lines 9 and 10.
A final point about the first loop in this program must be emphasized. The loop index in this for loop,
as well as in any for loop, will be treated as a constant within the loop, so you cannot assign a new
value to it. The looping mechanism itself will be the only way that the loop index can be changed in
value.
OUR FIRST ATTRIBUTES IN USE
In chapter 3, we took a brief look at attributes, but didn't really look at uses for them. In the loop in
lines 27 through 31, we have the range once again defined as the limits of the type named
MY_TYPE but this time we explicitly name the first and last values of the type by using the
attributes. The method depicted in line 21 is more concise, but both methods lead to the same result.
If you wanted to loop from the first element to some midpoint, for example, the second method gives
you a way to use one of the endpoints of the range.
CALCULATED LOOP RANGE LIMITS
The loop in lines 33 through 37 has range limits that are not static but that are calculated when the
loop is entered. In this case, the calculations are all based on constants, but they could be based on
variables of any arbitrary degree of complexity. If they are based on one or more variables, there is a
subtle point that you must understand. If one or more of the variables are changed within the loop, the
range does not change accordingly, because the loop range limits are calculated only once, when the
loop is entered.
It should be clear that the loop range is 2 through 5 for this particular loop.
A DOUBLY NESTED for LOOP
Lines 39 through 49 contain two nested for loops with an addition to the outer loop. The identifier
Named_Loop is a loop name which applies to the outer loop. The name appears prior to the start of
the loop and also follows the corresponding end loop for that loop. This results in a named loop. In
lines 42 and 43 we have a conditional statement that says if the product of the Height and the Width
are equal to 12, we are to exit the loop that is named Named_Loop. Without the name given we
would only exit the loop currently in effect, the inner one defined by the loop index Width, but since
we mention the name, we exit the loop with the given name, and exit both loops. By adding a name to
a loop, you can exit out of as many nested levels as you desire with a single exit statement.
A CLOSE LOOK AT THE LOOP INDEX
In line 51, we assign the value of 157 to the variable named Special_Index and use that variable
name for the loop index in the following loop. After the loop is terminated, we display the value of
the variable to see what the final value is, and when we run the program we find that the value did not
change from that assigned, namely 157. This would suggest that even if we think we are explicitly
defining the loop index variable, the system is actually making up an entirely new variable, using it,
and throwing it away after the loop is terminated. Ada defines a loop variable as an automatic
variable which is generated when needed and discarded when it is no longer needed. More will be
said about automatic variables later. The important point to grasp from this is that Ada implicitly
declares all loop variables for us.
LOOPS ARE IMPORTANT
You will have many opportunities to use all three forms of the loops discussed here, so it would pay
you to study these loops until you are sure of what they are doing.
Note the similarity of the three loops, and the fact that each is a complete Ada statement terminated
with a semicolon. When we study the next two controls, you will see that they are very similar, and
fit the same pattern.
loop <...> end loop;
while <BOOLEAN expression> loop <...> end loop;
for <loop index> in <range> loop <...> end loop;
Compile and execute this program taking care to observe the value of the variable named
Special_Index when the loop is complete.
This would be a good point for you to begin getting acquainted with the Ada 95 Reference Manual
(ARM). Study section 5.5 in the ARM which defines the Loop Statements in detail. You will find it
surprisingly easy to read and understand except for the syntax statements at the beginning of the
section. You will see that there is little to the loop construct that we have not covered in this section
of the tutorial, and you will begin getting familiar with the ARM. You will find the ARM to be a
valuable source of information after you gain some experience with Ada.
NOW FOR CONDITIONAL STATEMENTS
Example program ------> e_c05_p3.ada
We will spend some time now examining the program named e_c05_p3.ada and studying the
conditional statements. Notice here, that we don't even try to define any variables, but we will let the
system generate them for us as implicit loop indices. The Ada statement contained in lines 8 through
18 is a for loop which we are now familiar with, so we will use it to study how the if statement
works. Notice that we display the value of Index, then use it in the first if statement in lines 11
through 13. This statement says that if the value stored in Index is less than 4, then execute the
statements between the reserved words then and end if. In this case, we display the message in line
12 on the monitor. If the value stored in Index is not less than 4, we ignore the statement between the
reserved words and go directly to line 14, where we encounter another if statement. This one displays
a different line on the monitor if the value stored in Index is greater than 5. This is the simplest kind
of if statement, and you will find it to be extremely useful, in fact, you used it in the previous two
example programs. Note that the if condition must evaluate to a BOOLEAN value.
Moving ahead to the next loop, the one in lines 21 through 29, we find a more elaborate conditional,
the if then else statement. In this case, if the value of Index is less than 15, one message is displayed
and if not, a different message is displayed. In the prior loop, we used an if statement where a
message may or may not have been displayed, but in this one, we use an if statement where one of
the messages will be displayed every time. This, of course, is very similar to other programming
languages.
NOW FOR THE MULTIWAY if STATEMENT
The loop in the statement contained in lines 32 through 44 is a bit more involved since there are
several possibilities for selection of the line to be displayed. The word elsif is another reserved word.
In this case, one and only one of the statements will be executed during each pass through the loop.
The reserved word else is optional, and if it were not there, it would be possible that none of the
conditions would be met and therefore none of the messages would be output. As many statements as
desired can be placed between each of the reserved words such that a selection could cause a large
number of statements to be executed sequentially.
The last statement, given in lines 51 through 67, gives an illustration of nesting of loop and if
statements. It should cause you no difficulty to understand the nesting and when you compile and run
this program, you should have a good understanding of the output.
THE MULTIWAY CONDITIONAL CONSTRUCT
Example program ------> e_c05_p4.ada
Examine the program named e_c05_p4.ada for an example of a program with a case statement, the
multiway conditional statement in Ada. Lines 8 through 20 comprise a loop with a case statement
contained in it. Lines 12 through 18 contain the actual case statement. The variable How_Many is
the case selector variable and each time we pass through the loop, we enter the case statement with a
different value stored in the variable How_Many. When How_Many has the value of 4 through 6,
we select the first path, and that path only, when it has the value of 7 or 9, we select the second path,
etc. The vertical bar is the or operator here, and means if we have either of these cases, do this list of
statements. The case statement is composed of the reserved words case, is, when, and end case in the
manner shown. All cases must be accounted for in some way since it is an error to enter the case with
a value of the case selector variable for which there is not something defined for it to do.
WHAT IS THE => OPERATOR?
The "=>" operator is used in many places in an Ada program which we will see as we progress
through this tutorial. It can be loosely defined as a "do this" operator, because in most places it can be
read as "do this". For example in line 13, the line can be read "when How_Many is 4 through 6 do
this Put statement".
A special case is provided which is the others case. It is optional, but if it is included, it must be the
last one listed. This is illustrated in the other example in this program, the example in lines 27
through 37. This program is an illustration of more complex operations in each of the case branches,
and it is expected that you should have no difficulty discerning the operation of each of the cases.
This is one of the places where a null statement can be very useful as in line 35 where we really want
to do nothing and can explicitly tell the compiler so. Be sure to compile and execute this program
after you are sure you understand it. The case statement occurs rather infrequently in programming,
but you will use it, so you should understand it thoroughly.
This would be a good time to repeat a list which was given earlier in this chapter with the if and case
statements added to illustrate the symmetry of the control structures and how well thought out they
really are.
loop <...> end loop;
while <BOOLEAN expression> loop <...> end loop;
for <loop index> in <range> loop <...> end loop;
if <condition> then <...> end if;
case <selector> is <...> end case;
THE EVIL GOTO STATEMENT
Example program ------> e_c05_p5.ada
The program named e_c05_p5.ada illustrates use of the goto statement, a statement that has fallen
into ill repute among software developers in recent years. This is because the goto can be used very
abusively, leading to nearly unreadable spaghetti code. There may be instances when use of the goto
statement will result in very clear code, and when those cases arise, it should be used. It has been
proven that it is possible to write any logic without resorting to the goto statement by using only
iteration (looping), conditional branching, and sequential statements and every effort should be made
to rely on these three elements alone.
When Ada was being developed, the developers considered leaving the goto statement completely
out of the language, but it was included for a very nebulous reason. It was felt that after Ada became
a popular language, there would be a need to translate existing programs from older languages, such
as FORTRAN, into Ada using automatic translators. Such a job would be impossible without the
goto statement, so it was included in the Ada language. Modula-2 is an example of a language
structured very much like Ada, but without a goto statement defined as part of the language.
The goto is available as a part of the Ada language and is illustrated in the present example program.
It should be no surprise to you that goto is another reserved word. The rather messy looking double
"<<" and ">>" enclose the label to which you wish to jump and the rest should be easy for you to
discern. Be sure to compile and run this program.
FINALLY, A USEFUL PROGRAM
Example program ------> e_c05_p6.ada
Examine the program named e_c05_p6.ada for an example of a program that really does do
something useful, it generates a table of Centigrade to Fahrenheit temperature conversions, and it
outputs a special message at the freezing point of water and another at the boiling point of water.
Notice the program definition in the header that defines exactly what it does. You should have no
trouble understanding every detail of this program, except of course for the rather cryptic code in
lines 8 and 9. Although we have had a brief introduction to these, we will cover the details of them
later in this tutorial.
POOR CHOICE OF VARIABLE NAMES
Example program ------> e_c05_p7.ada
Compile and run e_c05_p6.ada, then examine the program named e_c05_p7.ada for an example that
uses some poor choices of variable names and omits the comments. This program is the same
program that you just studied because it does exactly the same thing, but I am sure you will find it a
little harder to follow since the names are not very useful in helping you understand what it is doing.
In the beginning of this tutorial, we studied a program named e_c02_p1.ada which was absolutely
ridiculous in style. No serious programmer would write such a program as that, but many
programmers seem to be happy using a style similar to the present example.
You should begin, even at this early point in your Ada programming development, to develop good
formatting skills. Remember that Ada was designed to be written once and read many times, so the
extra time spent keying in long meaningful identifiers will be worth it to your colleagues when they
find a need to study your code.
PROGRAMMING EXERCISES
1. Write a program with looping and conditional statements that will output the year and your
age during each year from the time you were born until you were 21. Include a special note the
year you started school and another note the year you graduated from High School. Example
output follows;(Solution)
In 1938, I was 0 years old.
In 1939, I was 1 years old.
...
...
In 1943, I was 5 years old, and started School.
...
etc.
2. Rewrite exercise 1 using a case statement.(Solution)
Advance to Chapter 6
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 6
ADDITIONAL SCALAR TYPES
OUR OWN TYPES
Most of the example programs in this tutorial have used the predefined type INTEGER for
illustrating various concepts, and it is an excellent choice due to its versatility. There are several other
types available because they are part of the Ada definition, and we can define our own types for
special purposes. This chapter will illustrate some of the reasons for doing so.
A complete description of types will be given in the next chapter but first we will learn how to use
some of them. We are caught in a dilemma like the proverbial, "which came first, the chicken or the
egg?", and must select which to define first. We have chosen to illustrate usage in this chapter, and
then give the details of type definition in the next chapter.
A FEW INTEGER CLASS TYPES
Example program ------> e_c06_p1.ada
Examine the file named e_c06_p1.ada for some examples of how and why we might use our own
type definitions. The three types illustrated in lines 7 through 9 are available with all Ada compilers
because they are so versatile and useful, and because they are required by the Ada programming
language. As we have said before, the type INTEGER defines a variable that can have any whole
value between -2,147,483,648 to 2,147,483,647 on most 32 bit computers. It should be pointed out
that some microcomputers use a much smaller range as standard. The type NATURAL defines a
variable from 0 to 2,147,483,647, and the type POSITIVE covers the range from 1 to 2,147,483,647
on most 32 bit computers.
Consider the word "most" in the last paragraph, and think about the problems you could have if you
wrote a program that depended on a particular variable covering the listed range, and tried to move
the program to a different machine which used a different range. You could be faced with a large
rewrite problem in order to get the program to work on the new computer.
HOW CAN WE HELP SOLVE PORTABILITY PROBLEMS
Suppose we defined our type to cover a certain range, such as illustrated in line 11 of this example
program, and moved the program to another computer. According to the definition of Ada, the new
compiler would be obligated to create a type for us that would cover the given range, or give us a
compile time error telling us that the hardware simply could not support the defined range. In this
case, due to the rather small range requested, any meaningful compiler and machine combination
would be able to cover the defined range, and we would have a program that would run in spite of
differences in the way the standard types were defined. Good programming practice, especially if the
source code may need to be moved to other computers, would define all ranges explicitly and avoid
the implementation defined limits built into the compiler.
Two new types are defined in lines 11 and 12, and the program uses some new attributes to illustrate
the new types. In lines 21 and 24, we use two attributes which we have used before, but in lines 28
and 31, we use two new attributes. In order for the system to create a type which covers a range of -
1000 to 24000, it must use a structure with enough binary bits to cover the given range. The range is
not composed of binary limits so the system will have to define enough bits to cover this range and a
little more. It will probably define some number of 8-bit bytes and the range covered by the full
pattern, as defined, is called the base range. The two new attributes give the limits of the base
selected by the system. The base limits will probably be -32768 to 32767 even if you are using a 32
bit system or it may even use -128 to 127.
DO YOU HAVE A SMART COMPILER?
The type illustrated here named MY_SHORT has defined limits of -12 and 127, a relatively small
range. It is small enough that it can fit into a base range of -128 to 127, which could be stored in a
single 8-bit byte. If your compiler is smart enough to realize that, it could use a single 8-bit byte to
store every variable of this type, and if you had a lot of these to store, it would save you a lot of
memory. You will probably find however, that most compilers will simply use the full INTEGER
range for the base type of even this small number. Four attributes of two different types are displayed
on the monitor for your information. You can see from the results of running this program exactly
how your compiler stores these two types.
THE in AND not in OPERATORS
We have a new operator to learn about now, the in operator illustrated in line 46. If the variable
Index, which has a current value of 345 due to initialization, is within the defined range of the
subtype MY_SUBTYPE, a BOOLEAN type TRUE will be returned, otherwise a BOOLEAN type
FALSE is returned. This result can be assigned to a BOOLEAN variable or used for a boolean
decision as shown. In this case, the value of Index is not in the range of MY_SUBTYPE so a
FALSE is returned and the message will not be output. Another operation is illustrated in line 50
which is the not in operation, and should be self explanatory. You should be able to see that the
message in line 51 will be displayed. The in and not in operators are further illustrated in lines 54
and 58 where an explicit range is used for the test range.
Be sure to compile and run this program and observe the output. Here is a chance for you to see if
you have a smart compiler. If you check the package named Interfaces, you will find that you have a
few other types available such as INTEGER_8, INTEGER_16, and INTEGER_32. These are all
optional, but some will surely be available with your compiler.
THE ENUMERATED TYPE
Example program ------> e_c06_p2.ada
Examine the program named e_c06_p2.ada for our first look at an enumerated type and how it is used
in a program. Line 7 is the first definition of an enumerated type, and uses the reserved words type
and is as shown. The type name is given between the two reserved words and the values which a
variable of this type is allowed to have assigned to it are given as a list within parentheses. The values
actually represent numerical values from 0 up to that value required for the largest value, in this case
6, since the numbering will be assigned from 0 to 6. In line 20, the variable named Day_Of_Week is
declared to be of type DAY, so it can be assigned any of the 7 values listed for the type DAY, and no
others. We could assign the values 0, 1, 2,.. 6 to represent the 7 days of the week and use the
numerical values within the program, but by using the enumerated type, we can refer to Sunday as
SUN, Monday as MON, etc., making the program much clearer and easy to follow.
There are three predefined enumerated types in the Ada language, BOOLEAN, CHARACTER, and
WIDE_CHARACTER. The BOOLEAN type is an enumerated type with two possible values, but it
has some special properties available with no other enumerated variables. These will be discussed
using the next example program.
Jumping ahead to the executable code in the current example program, we illustrate assignment in
line 28, where we assign the value of WED to the variable Day_Of_Week. Lines 29 and 30 illustrate
the FIRST and LAST attributes which we have seen before for INTEGER type variables. Just as -
2,147,483,648 is the lowest possible value that can be assigned to a 32 bit INTEGER type variable,
MON is the lowest, and hence the first, value that can be assigned to a variable of type DAY.
TWO NEW ATTRIBUTES
Lines 31 and 32 illustrate the attributes PRED, which means the predecessor, and SUCC, which
means the successor. PRED returns the value of the predecessor of the present value of the variable
used as an argument. Since the variable Day_Of_Week was assigned the value of SUN in line 30,
and the day just prior to SUN is SAT, SAT is assigned to the variable Day_Of_Week in line 31. It is
an error to attempt to take the PRED of a variable which contains the first value in the available list,
and will result in raising the exception Range_Error. Likewise, an attempt to take the SUCC of any
variable that is at its maximum value will result in the exception Range_Error being raised.
Exceptions will be covered in detail later in this tutorial. At this time simply remember that an
exception refers to an exceptional condition or an error.
WHAT IS A SUBTYPE OF AN ENUMERATED TYPE?
In lines 8 and 9, we define two subtypes of the type DAY which will have all the characteristics of
type DAY except for a more restricted range. A variable that is declared to be of type PLAY_DAY
can be assigned either of two values, SAT or SUN. SAT will have a numerical value of 5, and SUN
will have a numerical value of 6, both of these being inherited from the parent type, DAY. Thus in
line 32, we use the attribute FIRST to get the first day of type PLAY_DAY, which will be SAT,
then use the attribute SUCC to get the successor of that value, which will be SUN. Notice how the
attributes can be combined to obtain the needed information. A subtype is assignment compatible
with its parent type. We will discuss subtypes in greater detail in the next chapter of this tutorial.
NOW FOR THE POS AND VAL ATTRIBUTES
The POS attribute will return a value of type universal_integer, the value representing the position of
the enumerated value within the parentheses as shown in lines 33 and 34. The VAL attribute will
return the enumerated value of the numerical value included in the parentheses. Notice that if the type
DAY in line 35 were changed to PLAY_DAY, an error would be returned, since that is an illegal
enumerated value for that type. The error would be returned by raising the exception Range_Error.
WHAT ABOUT ENUMERATED ASSIGNMENTS?
The value of Happy_Day can be assigned to Day_Of_Week at any time because they are type
compatible, and any value that can be legally assigned to Happy_Day can also be assigned to
Day_Of_Week. Day_Of_Week cannot always be assigned to Happy_Day however, because
Day_Of_Week is permitted to contain some values which are not legal to assign to Happy_Day.
This would illustrate that some care must be exercised when using the enumerated type, but if used
properly, it can help in program debugging by the use of the strong type-checking defined into the
Ada language.
USING ENUMERATED TYPES FOR CONTROL
The loop in lines 37 through 40 covers exactly the range covered by the subtype WORK_DAY, so
we can use it in the range part of the definition of the loop. When you run this program, you will see
that the loop will be executed exactly five times.
Lines 42 through 51 contain two relational checks on the variable Today to illustrate that the
enumerated type variable can be used in a BOOLEAN expression. All of the boolean operators are
available, which includes the following list, and no others;
= equality
/= inequality
> greater than
>= greater than or equal to
< less than
<= less than or equal to
No mathematical operations are available with enumerated type variables. Assignments are available
as illustrated in the present example program.
WHAT IS QUALIFICATION?
In lines 53 and 54, we assign the same value to two different enumerated type variables. At least it
seems to be the same value. In actuality, they are two different values with the same name, namely
SUN. Because Ada does such strong type checking, it is smart enough to realize that they are actually
two different constants and it will select the one that it needs for each statement based on the type of
the variable to which it will be assigned.
Lines 56 and 57 make the identical assignments by qualifying which value you are interested in, but
in this case, the qualifications are unnecessary. There could be a case when you would need to tell the
system which value of SUN you are interested in. Qualification uses the type followed by a "tick", or
apostrophe, prior to the enumeration value.
OUTPUTTING ENUMERATED VARIABLES
The statements in lines 59 and 60 output the current value of the variable Today, and the predecessor
of the current value. Finally, the same values are output for the variable Big_Sphere, and when you
run the program, you will see that the same value is output for the first value in each line, but the
second values differ for the two variables. Note the four extra lines of code given in program lines 14
through 18. These are used to tell the system how to output enumerated variables, and we will cover
the essentials of how this works very soon.
The in and not in operators which we studied in the last program are available for use with the
enumerated type variable. In fact, they are available with all discrete types. Be sure to compile and
run this program and after studying the results, see if you can modify the program to output
additional enumerated values.
THE BOOLEAN VARIABLE
Example program ------> e_c06_p3.ada
The program named e_c06_p3.ada is an illustration of how to use the BOOLEAN variable, which is
actually a special case of an enumerated variable. It is simply an enumerated type with two possible
values, TRUE or FALSE. Since it is an enumerated type, all of the operations available with the
enumerated type are available with the BOOLEAN type, including all six of the relational operators,
the assignment operator, the attributes, and no mathematical operators.
THE LOGICAL OPERATORS
The BOOLEAN type has some of its own unique operators that are available with no other types,
and these are the logical operators. The logical operators were defined earlier, but are repeated here as
a complete list.
and logical and operation
or logical or operation
xor exclusive or of two values
not inversion of the value
and then short circuit and operation
or else short circuit or operation
It should be pointed out that FALSE is of less numerical value than TRUE, by definition, and in
actuality, the value of FALSE is 0, and the value of TRUE is 1.
The program illustrates how to output BOOLEAN values in lines 29 and 30, after the package
instantiation in lines 7 and 8. Notice that the Enumeration_IO library is used for BOOLEAN output
illustrating again that BOOLEAN is a special case of the enumerated type.
Be sure to compile and execute this program to see that it really does compile and execute as stated.
Someday, you will need the ability to display the BOOLEAN results as is done in this program.
SOME USELESS ATTRIBUTES OF INTEGER TYPES
Example program ------> e_c06_p4.ada
It may seem silly to illustrate some useless attributes but the program named e_c06_p4.ada does that
very thing. The POS attribute of an integer variable is defined as being the number itself, and the
VAL is also the number. The SUCC of an integer variable is the next number, and the PRED is the
predecessor. These last two attributes could be useful for incrementing or decrementing a variable in
a program, but good programming practice would forbid such a use of these attributes. You should
use the very clear and easy to understand method of adding one to the value and assigning the result
back to the variable, as illustrated in line 17 of the program.
Even though these are really useless at this point in time, the fact that this can be done will be very
useful when we get to the study of generics later in this tutorial.
Compile and run this program, adding some output to gain some of your own programming
experience.
MODULAR TYPE VARIABLES
Example program ------> e_c06_p5.ada
Ada 95 introduced a new type into the language, the modular type variable which can be very useful
in certain kinds of programs. In line 7, we define the type DIAL_RANGE as mod 5, which means
that any variable declared to be of this type will "modulo" when it reaches the value of 5. The
variable declared in line 8, namely Dial, can store only values in the range of 0 to 4, and when it is
incremented past 4 it modulo's back to zero with no error indication of overflow. Any value from 0 to
4 can be added to it, or subtracted from it and it will modulo at either the upper or lower end of it's
permitted range. A modulo variable cannot store a negative number. It is essentially an unsigned
INTEGER with the added provision that it cannot overflow or underflow.
The type declared in line 10 is another mod variable named MY_BINARY_BIT which is only
permitted to store a value of 0 or 1, and a variable of this type named My_Bit is declared in line 11.
Two other types are defined in lines 13 and 14 for illustration, but they are not actually used in this
program.
In lines 16 through 20, we instantiate modulo output packages for these variable types and use them
in the main program. We use an incrementing loop, followed by a decrementing loop to illustrate
both overflow and underflow with these two new types. The program should be very easy for you to
follow and understand, so nothing more needs to be said about it.
There are some predefined modular types available such as UNSIGNED_8, UNSIGNED_16, and
UNSIGNED_32. They are defined in the package Interfaces along with several bitwise and, or,
xor, etc. operations, and several shifting and rotating subprograms.
FLOATING POINT VARIABLES
Example program ------> e_c06_p7.ada
Examine the program named e_c06_p7.ada for examples of nearly all operations possible with
floating point numbers.
We begin, in lines 7 and 8, by defining two constants, the second being defined in terms of the first.
Remember that any thing used in an Ada program must be previously defined, and you will know all
of the rules for defining a value in terms of some other value. The two constants are of type
universal_real, so they can be used with any of the various real types we will encounter in this
program. We declare a variable named R in line 10 of type FLOAT, which is defined by the
compiler writer, then two new types of floating point numbers, and we finally declare six variables of
various types.
Two additional floating point types, SHORT_FLOAT and LONG_FLOAT are defined as optional
by the ARM and may be available with your compiler. You can find out by checking the
documentation supplied with your compiler or by declaring variables of those types to see if the
compiler will accept the declarations. If they do exist, you can determine their limits by using
attributes as defined below. You can also determine their limits by studying Annex M which is
required to be supplied with every Ada 95 compiler. Annex M contains the definition of all
implementation dependent entities.
HOW TO DECLARE A NEW FLOATING POINT TYPE
The reserved word digits used in line 12 tells the compiler that you don't care how many bytes of
storage it uses to define the number, but it must store at least 7 significant digits for every variable of
type MY_FLOAT. Line 13 requests a minimum of 15 significant digits for every variable of type
MY_LONG_FLOAT. The type FLOAT as used in line 10, on the other hand, only requires that it
be a floating point number, and the compiler writer has the option of using as many significant digits
as he desires to implement variables of this type. If you wrote a program that ran well with one
compiler, it may not run properly with a different compiler, either because the new one did not use
enough significant digits, or because the new one used far more, causing your program to run out of
storage space or run too slowly. The forms in lines 12 and 13 are therefore preferred for portability
purposes. More will be said about declaring floating point types in the next chapter of this tutorial.
FLOATING POINT LITERALS
The distinguishing characteristic that defines a floating point number is the use of a decimal point.
Ada requires at least one digit before and after the decimal point in all floating point literals, although
either or both may be a zero. Single embedded underlines are allowed to improve readability, but
cannot be adjacent to the decimal point. The underlines are ignored by the compiler, and have no
significance. Any radix from 2 through 16 may be used by first giving the radix, then enclosing the
number in pound (#) characters. The base 10 is the default and need not be specified. Exponential
notation can be used, the exponent being to the same base as that indicated by the radix. A binary
floating point literal is illustrated in line 31 of the program and you can see that the radix is similar to
that used for integer class literals.
FLOATING POINT MATHEMATICAL OPERATORS
Lines 29 through 35 illustrate the mathematical operators available with floating point variables and
should be self explanatory with the exception of the exponential operator. This can use only an
integer type of exponent but it can be either positive or negative. Of course, zero is also permissible.
All six logical comparisons are available with floating point variables as illustrated in lines 38
through 43. Keep in mind that it is bad practice to compare two floating point numbers for equality or
inequality in any language, but it can be done in Ada. The next two lines, 45 and 46, illustrate some
multiple mathematical operations.
As with all variables, the types must agree within all mathematical and logical operations and the
result must be assigned to the right type of variable, or a type error will be generated at compile time.
In line 46, the variable Area must be transformed in type prior to being assigned to Cover since they
are of different types. The entire statement will be evaluated as of type MY_LONG_FLOAT, since
that will be the final result. The constant 27.3, and the constant TWO_PI, will be transformed
automatically from universal_real to MY_LONG_FLOAT prior to the multiplications.
NOW TO OUTPUT SOME FLOATING POINT VALUES
In lines 22 through 25 we instantiate a copy of the Integer_IO package and a copy of the Float_IO
package for use with the types INTEGER and MY_FLOAT and use it in lines 49 through 55. The
variable Area will be output in a default exponential notation in line 49, but with 5 digits prior to the
decimal point in line 50. Line 54 adds an additional 5, which will cause 5 digits to be output
following the decimal point, and the fourth field, in this case a zero, causes the output to be written
with a zero exponent, or no exponential notation.
FLOATING POINT ATTRIBUTES
Floating point variables and types are no different from the scalar types concerning attributes
available for your use, except that there are different attributes available. Lines 58 through 66
illustrate the use of some of the available attributes. The attribute named DIGITS, gives the number
of significant digits available with the specific type, and the return is a universal_integer type.
The attributes named SMALL and LARGE give the smallest and largest numbers available with the
corresponding type, and the attributes named FIRST and LAST combined with the BASE attribute
as shown in lines 62 and 65, define the extreme values as used by the underlying base type of the
actual user's type. All four of these attributes return a value of the type universal_real, and are
displayed on the monitor for your information. Note that there are other attributes available with the
floating point type, but only these will be elaborated upon at this time.
See Annex K of the ARM for additional information on attributes. This appendix lists all of the
attributes available with an Ada system.
Compile and run this program and observe the output. The actual output is the clearest description of
the Put procedure when used with floating point numbers. Study the result of the attribute outputs
before continuing on to the next example program.
FIXED POINT VARIABLES
Example program ------> e_c06_p8.ada
Fixed point variables are a relatively new concept and may be a bit confusing, but the file named
e_c06_p8.ada will illustrate the use of a few fixed point variables. Line 9 defines a fixed point type as
having a range of -40.0 to 120.0, and a delta of 0.1 which means that a variable of this number can
only have a value that is accurate to one digit after the decimal point. There are therefore a fixed
number of digits before and after the decimal point, hence the name of this type of variable.
A fixed point number will always be exact since it is defined that way. There can never be a gradual
accumulation of error with a fixed point variable. In order to completely understand the fixed point
type, one would require a complete understanding of numerical analysis, which is beyond the scope
of this tutorial. The program before you will illustrate how to use this type, but no attempt will be
made to explain why it should be used.
There are no predefined fixed point types, so it is up to the programmer to define every fixed point
type needed, as illustrated in lines 9 and 10. The reserved word delta denotes a fixed point type and a
range is required for every fixed point type. Lines 12 and 13 are used to declare a few variables for
use in the program, then lines 17 through 20 instantiate the package Fixed_IO for use with our two
fixed point types.
HOW DO WE USE FIXED POINT TYPES?
Output of a fixed point type uses the same format as that defined for floating point data as shown in
line 27. When we come to arithmetical operations, we find some funny rules which we will simply
state, and make no attempt to justify. Variables of the same fixed point types can be added and
subtracted freely, provided the results are within the defined range, just like floating point type
variables. Multiplication by a constant of type universal_integer is permitted, resulting in the same
fixed point type we started with. Multiplication of two fixed point variables results in an anonymous
type which must be explicitly converted to some predefined type, as illustrated in lines 33 and 34.
The only operator available with the fixed types is the abs operator.
Many attributes are available with the fixed point type, some of which are illustrated in lines 46
through 55. The attributes named DELTA, SMALL, and LARGE, each return a value which is of
type universal_real, and must be converted to the users fixed type, by a type conversion, before the
result can be used in the program. Line 48 illustrates the conversion within the Put procedure call.
Be sure to compile and run this program and observe the output to see if it conforms to what you
think it should do based on the previous discussion. Note that your compiler may not generate
identical output as that listed in the result of execution due to different compiler defaults.
ANOTHER NEW TYPE
Example program ------> e_c06_p9.ada
A new type was added to the language with the introduction of Ada 96, the decimal type. It looks and
acts very much like any other fixed type with the added limitation that it can only use a delta that is a
power of 10. Every thing that can be done with a fixed type can be done with the decimal type, and
all of the limitations of fixed types are applied to the decimal type also.
The decimal type is intended for financial software and has one advantage over the fixed type, and
that is the predefined package named Ada.Text_IO.Editing which is a part of the optional
Information Systems Annex. This package provides formatting monetary values with the dollar sign,
commas, and proper placement of the decimal point. The example program illustrates use of the
decimal type, but it does not illustrate the use of the optional annex since it is expected to be
supported by only a few Ada compilers, those targeted to the banking and financial industry.
MIXING VARIOUS TYPES
Example program ------> e_c06_p10.ada
Examine the program named e_c06_p10.ada for examples of using various types together. It is meant
to be an illustration of how to combine some of the various types available in Ada. Many type
transformations are illustrated by the explicit conversions in this program and should be easy for you
to understand. Note especially, that the final result of lines 27, 28, and 29 will not necessarily be the
same due to the rounding that takes place at different points in the calculations.
Note that in Ada, conversion from real to integer always rounds rather than truncates. A value
midway between the two integer values will always move away from zero. This is carefully specified
in Ada 95, but was not defined for Ada 83.
Compile and execute this program to assure yourself that it will compile correctly.
This would be a good time to get a little more familiar with the ARM by examining sections 3.5.4
and 3.5.7 which define the requirements for INTEGER and FLOAT type variables. You will not
understand it all, but you will understand enough of it to make it a profitable task.
PROGRAMMING EXERCISES
1. Write a program to determine if LONG_INTEGER and SHORT_INTEGER types are
available with your compiler. If they are available, use attributes to determine their
characteristics.(Solution)
2. Do the same thing as exercise 1 for the LONG_FLOAT and SHORT_FLOAT types.
(Solution)
3. Try to take the PRED of the first element of an enumerated variable to see what kind of a run-
time error you get. Your compiler may be smart enough to warn you if you try to take it
directly (i.e. - by using the first value in the parentheses), so you may need to assign a variable
to the first value and take the PRED of the variable.(Solution)
Advance to Chapter 7
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 7
DERIVED TYPES
LET'S TALK ABOUT TYPES
We have been talking about types throughout this tutorial, but we've been sort of working our way
around them rather than carefully defining the various kinds of types available in Ada. This chapter
will be devoted to a full discussion of types. It was not possible to discuss types in full until we
covered a bit of material about the various scalar types and how they are used, but with that material
behind us, we will give a full treatment to the topic of types.
There are four different ways to declare a type. You can probably guess that we will discuss each of
the ways in detail in this chapter. They are listed as follows;
1. Predefined types. These are provided for us by the compiler writer as defined in the Ada 95
Reference Manual (ARM). We have already used several of these.
2. User defined types. We have already defined a few new types in some of the earlier programs.
3. Derived types. These get their name because they are defined in part based on a previously
defined type and derive some of their characteristics from those types. We have not yet
encountered the derived type.
4. Subtypes. These are usually a subset of another type upon which they are based. We
encountered subtypes in the last chapter.
A type defines a set of values and a set of primitive operations. The primitive operations of a type
are;
G The intrinsic predefined operations such as assignment, addition, subtraction, etc.
G Any operations inherited from a parent in the case of derived types. We will study derived
types in this chapter.
G Subprograms with one or more parameters or a result of the type that are declared within the
same package as the type.
PREDEFINED TYPES IN ADA
The ARM requires every Ada compiler to provide several predefined types, including INTEGER,
NATURAL, POSITIVE, FLOAT, CHARACTER, and STRING. Several other types are optional
and include LONG_INTEGER, SHORT_INTEGER, LONG_FLOAT, and SHORT_FLOAT. It
will be left to you to study the documentation that came with your compiler and see which of the
optional types are included with your compiler package. You will find this information in Annex M.
The actual definition of these types is given in the package named Standard.
The predefined types can be used as the basis of the declaration of new types for your specific
application and is the topic of this chapter.
USER DEFINED TYPES
Example program ------> e_c07_p1.ada
Examine the program named e_c07_p1.ada for several examples of user defined types. Lines 7
through 9 each declare an entirely new type. Considering only the predefined type INTEGER and
these three, we have four types to use in the program, and each type has nothing to do with the other
three. Variables of these types cannot be intermixed in any way without the proper type conversion
explicitly stated by the programmer. Moreover, variables declared with one of these types cannot be
assigned a value that is outside of its declared range. The structure for declaring a user defined
integer class type is given as,
type <type-name> is range <lower-limit>..<upper-limit>;
As stated earlier, the word range is a reserved word, and its presence indicates to the compiler that
you wish to have a type of the integer class.
Use of the three new types is not illustrated in this example program, but the diligent student will
have no trouble using these new types to declare a few variables, then use them in some mathematical
statements.
DERIVED TYPES ARE RELATIVELY NEW
Derived types are an entirely new subject, since they are not available in any of the more popular
languages that you may have been programming in. Derived types get their name because they are
derived from an existing type rather than being an entirely new creation. A derived type has all of the
operations available that are available with the parent type but it may have a more limited range than
the range of the parent type. The example program has an illustration of this in line 10 where the type
TINY_POS is derived from the user defined type POS_INT but with a slightly tighter allowable
range. Any operation that is legal to be performed on a variable of type POS_INT is legal for a
variable of type TINY_POS.
The key to a derived type is the use of the reserved word new along with the type from which the
new type will be derived. The structure for declaring a user defined derived type is given as,
type <type-name> is new <existing-type>;
The derived type will be of the same class as the type from which it is derived.
We continue declaring new types in lines 11 through 13 and because these three are entirely new
types, each derived from the parent type INTEGER, they cannot be added together, subtracted,
compared or even assigned to each other without a bit of extra trouble. Each of the seven new types
in lines 7 through 13 share all of the operations that are defined for INTEGER, including the
arithmetic, logical, and assignment operations, but the operations can only be performed as long as
the types of the objects are consistent. Of course, the explicit type conversion can be done to allow
the combination of variables of any of these types.
The type TREE_INT, defined in line 13, is a derived type with a more limited range than the parent
type so it has all of the characteristics of the parent type except that it cannot be assigned a value
outside of its defined range.
HOW TO USE SOME OF THE NEW TYPES
As an example of using the new types, consider the type SALAD_INT which is a derived type of the
parent type INTEGER. Since three variables, Salad, Lettuce, and Tomatoes, are all of the same
type, they can be freely added, compared, assigned to each other, or used in any way legal for integer
class variables. They can be used as the range limits in a for loop. The three variables have the same
range as the parent type INTEGER, namely -2,147,483,648 to 2,147,483,647 on most 32 bit
machines. Constants of type universal_integer can be added to, compared to, or assigned to these
three variables.
HOW CAN THESE NEW TYPES HELP IN A PROGRAM?
Everything that was said about type SALAD_INT in the last paragraph is true of type
ANIMAL_INT, TREE_INT, or any of the other four types. Suppose somewhere in our program we
tried to add the number of Tomatoes to the number of Dogs and assign the result to Trees. If the
variable names are meaningful, we would probably not want to do such an operation in any practical
program. The Ada compiler would give us a compile time error, so we would detect the error before
we tried to run the program with such a silly statement. Careful assignment of types can be used to
protect us from the silly little errors that we are all so prone to make. It would be much more efficient
to let the compiler find these silly little errors and free us up to find the analysis errors we also make.
HOW DO YOU DO A TYPE TRANSFORMATION?
Even though you set things up very carefully, you may need to perform some operations on the data
where you actually do need to add the number of Animals to the total of Oak plus Coconut. Line 30
illustrates how to do this. Enclosing the variable in parentheses and adding the desired type to the
front of the grouping will change the type from the variable's actual type to the type in front of the
parentheses, but only for that one place in the program.
In line 31, Trees and Salad are both transformed to type INTEGER before being summed and
assigned to Count, a variable of type INTEGER. Line 33 illustrates the addition of many variables
by first transforming each to type SALAD_INT then performing the addition. In line 40, the same
variables are added together, but in this case, all variables are transformed into type ANIMAL_INT
prior to summing, then the sum is transformed to type SALAD_INT. The two methods should result
in the same answer, and you can verify that they do when you compile and run the program.
WHAT IS A SUBTYPE?
A subtype is a new type based on a parent type but usually with a more restricted range. It inherits all
of the characteristics of the parent type and in addition, it can be freely intermixed with the parent
type in calculations and assignment statements. The reason for using a subtype is usually to declare a
variable of the parent type but with a more limited range to take advantage of the range checking
capability of Ada.
WE CAN ALSO HAVE SUBTYPES OF DERIVED TYPES
Example program ------> e_c07_p2.ada
The program named e_c07_p2.ada gives an example of the definition and use of a subtype of a
derived type, and a derived type of a subtype. In line 7 we declare a derived type and in line 10 we
declare a subtype of the new derived type. The subtype of the derived type has the same
characteristics as the derived type except that it has a more restricted range in this case. Variables of
type NEW_SUBTYPE are compatible with variables of their parent type NEW_INT. This is
illustrated in line 26.
In this case, NEW_SUBTYPE is as different from the type INTEGER, as SALAD_INT was in the
last program.
WE CAN HAVE A DERIVED TYPE OF A SUBTYPE
Line 12 illustrates the declaration of a derived type based on using a subtype for the parent type. Note
that the new derived type has all of the characteristics of its parent type except for the more restricted
range, but once again, it is an entirely new type as far as type checking is concerned.
A SUBTYPE CAN BE SIMPLY A SYNONYM
Line 10 illustrates a subtype which covers the entire range of its parent type. Since variables of this
subtype can be freely intermixed with variables of its parent type, the subtype name is simply a
synonym for the parent type name.
With the discussion of the last program fresh in your mind, you should breeze through the remainder
of this program. Be sure to compile and execute it.
USING OTHER PREDEFINED TYPES FOR THE PARENT
Example program ------> e_c07_p3.ada
Examine the program named e_c07_p3.ada for examples of derived types and subtypes based on
some of the other predefined types in Ada. We begin by declaring two user defined types in lines 8
and 9 which are of the floating point class of types because of the reserved word digits appearing in
the definition. In line 10 we declare DER_FLOAT which has all of the characteristics of the
predefined type FLOAT, except that the compiler will consider it to be an entirely different type and
will not allow mixing of these two types. Of course, type conversion can be used if necessary.
The derived type LIM_FLOAT is declared with all the characteristics of FLOAT except that it has a
limited range to allow for compiler checks. Line 12 contains the definition of a subtype based on
DER_FLOAT with a more limited range. Variables of the type SUB_FLOAT can be freely
intermixed with variables of type DER_FLOAT, but not with those declared with the types FLOAT,
NEW_FLOAT1, NEW_FLOAT2, or LIM_FLOAT.
Lines 15 through 19 illustrate the same principles applied to fixed point types and should be self
explanatory. The only difference is the use of the reserved word delta in the fixed point definitions.
Lines 22 through 29 illustrate the declaration of derived types and subtypes of the CHARACTER
type and an enumerated type. These statements will be left to the students study since they are so
similar to the example using FLOAT as the parent type. We will study the CHARACTER type in
chapter 11.
A few variables are declared and some are initialized in lines 32 through 35, and a nonsense
calculation is given in line 39 to illustrate the type transformations that can be done with derived
types.
Be sure to compile and execute this program even though it has no output.
WHAT IS A CLASS IN ADA?
A class in Ada is the entire group of various concrete types that descend from a common ancestor, or
a common root. The name of the root is used as the name of the class. Therefore in the program
named e_c07_p3.ada, the NEW_FIXED1 class consists of the types NEW_FIXED1, DER_FIXED,
LIM_FIXED, and SUB_FIXED, because the last three are somehow derived from NEW_FIXED1.
In a similar manner, the class named DAY consists of the types DAY, WEEKDAY, and
BOWLING_DAY.
A WORD OF SUMMARY ABOUT TYPES
We have seen that in addition to the predefined types, we can declare additional types for use in our
programs. We can then use any of the predefined or user defined types as the parent type for either
subtypes or derived types. The new subtype or derived type can be used as a parent type for
additional subtypes or derived types and we find that we have a tremendous amount of flexibility in
defining the data to solve any particular problem.
PROGRAMMING EXERCISE
1. Modify the program named e_c07_p1.ada to include a new instantiation of the package Ada.
Text_IO.Integer_IO to output the variable named Salad in line 37 and 44 without the type
conversion.(Solution)
Advance to Chapter 8
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 8
SUBPROGRAMS
LET'S LOOK AT A PROCEDURE
Example program ------> e_c08_p1.ada
Ada was designed to be very modular, and we come to the point where we will study the first and
simplest form of modularization, the procedure. If you examine the program named e_c08_p1.ada,
you will have your first example of a procedure. Some languages call this kind of subprogram a
subroutine, others a function, but Ada calls it a procedure.
THE PROCEDURE IS LIKE THE MAIN PROGRAM
Beginning with the executable part of the main program, we have two executable statements in lines
14 and 15, each calling a procedure to write a line, if the name has any meaning. As always, the two
statements are executed in succession, and each statement is a procedure call to which we would like
to transfer control. The procedure itself is defined in lines 7 through 11, and a close inspection will
reveal that the structure of the procedure is very similar to the structure for the main program. It is in
fact, identical to the main program, and just as we begin executing the main program by mentioning
its name, we begin executing the procedure by mentioning its name. The procedure has a declarative
part, which is empty in this case, and an executable part which says to display a line of text and return
the cursor to the beginning of the next line.
When execution reaches the end statement of the procedure, the procedure is complete and control
returns to the next successive statement in the calling program.
Everything we have said about the main program, which is actually a procedure, is true for the
procedure. Thus we can define new types, declare variables and constants, and even define additional
procedures in the declarative part of this procedure. Likewise, we can call other procedures, and do
assignments and compares in the executable part of the procedure.
ORDER OF DECLARATIONS
The original Ada specification, Ada 83, required that all procedures must come after all type,
constant, and variable declarations. This was to force you to arrange your program such that all of the
smaller declarations must come first, followed by the larger ones, so that the smaller declarations
would not get lost by being buried between the larger declarations. This has been removed from the
requirement for Ada 95, and you are permitted to list the various entities in any order you desire.
The embedded procedure has all of the flexibility and requirements as those defined for the main
procedure. We will give further examples of each of these points in the next few example programs.
At this time, compile and run this program, and make sure you understand its operation completely.
THREE PROCEDURES
Example program ------> e_c08_p2.ada
Examine the program named e_c08_p2.ada, and you will see three procedures in the declarative part
of this program. Jumping ahead to the main program, beginning in line 30, you will see that the
program will write a header, write and increment something 7 times, then write an ending statement.
Notice how the use of descriptive procedure names resulted in our understanding of what the program
will do without looking at the procedures themselves. The names are long, and a bit tedious to type
in, but since Ada was designed to be a language that would be written once and read many times, the
extra time will pay off when the source code is studied by several persons in the future.
WHAT IS A GLOBAL VARIABLE?
The variable named Counter, defined in line 7, is a global variable because it is defined prior to any
procedure. It is therefore available for use by any of these procedures and it is, in fact, used by two of
them. In line 11, the variable Counter is assigned the value of 1 when the procedure named
Write_A_Header is called. Each time Write_And_Increment is called, the current value of
Counter is written to the display, along with a message, and the value is incremented. Note carefully
that the procedures are not executed in the order they are because of their relative order in the
declaration part of the program, but because the main program calls them in that order. The program
would work exactly the same way if you moved the first procedure, in its entirety of course, after the
procedure Write_An_Ending_Statement. The order of execution is controlled by the order of calls,
not the physical order of the procedures.
WHY DO PROCEDURES GO IN THE DECLARATIVE PART?
The declarative part of the program is where we define entities for use within the executable part. The
procedures are actually definitions of how to do something, so they are right where they belong.
Compile and run this program and be sure you understand the output.
A PROCEDURE WITH PARAMETERS
Example program ------> e_c08_p3.ada
Examine the file named e_c08_p3.ada and you will find a procedure that requires some data to be
supplied to it each time it is called. Three variables are defined as type INTEGER and used in the
procedure call in line 25. We will ignore the strange looking constructs in lines 9 through 12 for a
couple of paragraphs. The procedure header, beginning in line 15, states that it is expecting three
parameters, and each must be of type INTEGER, so we are compatible so far. When the procedure is
called, the value of the first variable, which is named Dogs in this case, is taken to the procedure,
where the procedure prefers to refer to it by the name Variety1. In like manner, the value of Cats is
given to the procedure, and is called Variety2. The variable named Animals is referred to by the
name Total in the procedure. The procedure is now ready to do some meaningful work with these
variables, but is somewhat limited in what it can do to them because of the mode field of the
procedure header.
THE MODE OF A PARAMETER
The formal parameter, as it is called in the procedure header, named Variety1, is of mode in which
means that it is an input parameter, and therefore cannot be changed within the procedure. Variety1,
along with Variety2 for the same reason, is a constant within the procedure, and any attempt to
assign a value to it will result in a compile error. The formal parameter named Total, however, is of
mode out and can therefore have a new value assigned to it within the procedure. Ada 83 defined that
the out mode variable could not be read, but Ada 95 has relaxed this requirement and permits you to
read the out mode variable within the procedure. Extra care must be taken that you do not attempt to
read the value of an out mode parameter that has not yet been initialized to some meaningful value.
Parameters that are of the modes in or in out do not have this problem since they are initialized, by
definition, when the procedure is called.
If a parameter is defined as being of mode in out, it can be both read from and written to. If no mode
is given, the system will use mode in as a default.
PARAMETER MODE SELECTION
All variables could be defined as mode in out and there would be no problems, since there would be
maximum flexibility, or so it would seem. There is another rule that must be considered, and that rule
says that every parameter that is of mode out or in out must be called with a variable as the actual
parameter in the calling program. This permits the value to be returned and assigned to the variable.
A variable of mode in can use a constant or a variable for the actual parameter in the calling program.
We have been using the New_Line procedure with a constant in it, such as New_Line(2), and if it
had been defined with the formal parameter of mode in out, we would have had to define a variable,
assign the value 2 to it, and use the variable in the call. This would have made the procedure a bit
more difficult to use, and in fact, somewhat awkward. For this reason, the formal parameter in
New_Line was defined using mode in. You should choose the mode of the formal parameters very
carefully.
The three formal parameters are available for use in the procedure, once they are defined as
illustrated, just as if they had been defined in the declarative portion of the procedure. They are not
available to any other procedure or the main program, because they are defined locally for the
procedure. When used however, their use must be consistent with the defined mode for each.
SOME GLOBAL VARIABLES
The three variables declared in line 7 can be referred to in the procedure as well as in the main
program. Because it is possible to refer to them in the procedure, they can be changed directly within
the procedure. The variable Animals can also be modified when control returns to the main program
because it is declared as out mode in the procedure header. This possibility can lead to some rather
unusual results. You should spend some time thinking about what this really means.
THE PROCEDURE SPECIFICATION
Lines 10, 11, and 12, give an example of a procedure specification. This is an incomplete procedure
declaration that you will find useful when you begin writing larger programs. The procedure
specification can be included for any procedure, if desired, and it describes the external interface to
the procedure without declaring what the procedure actually does. The Pascal programmer will
recognize this as being very similar to the forward declaration. More will be said about this topic
later. Note that the procedure specification is not required in this case, it is only included as an
illustration.
Compile and run this program after you understand the simple addition and assignment that is done
for purposes of illustration.
PROCEDURES CALLING OTHER PROCEDURES
Example program ------> e_c08_p4.ada
The example program e_c08_p4.ada contains examples of a procedure calling another procedure,
which is perfectly legal if the called procedure is within the scope of the calling procedure. Much
more will be said about scope later in this tutorial. The only rule that will be mentioned here is that
the procedure must be defined prior to a call to it. You should have no trouble understanding this
program, and when you do, you should compile and execute it. Be sure you understand where each
line in the output comes from and why it is listed in the order that it is.
HOW DO WE NEST PROCEDURES?
Example program ------> e_c08_p5.ada
Examine the program named e_c08_p5.ada for examples of nested procedures. We mentioned earlier
that it was possible to embed a procedure within the declarative part of any other procedure. This is
illustrated in lines 9 through 20 where the procedure Second_Layer is embedded within the
procedure Triple. In addition, the procedure Second_Layer has the procedure Bottom_Layer
embedded within its declarative part in lines 11 through 14. Such nesting can continue indefinitely,
because there is no limit to the depth of nesting allowed in Ada.
VISIBILITY OF PROCEDURES
There is a limit on visibility of procedures. Any procedure is visible, and can therefore be called, if it
is within the top level of the declarative part of the calling procedure. Any procedure is also visible if
it is prior to, on the same level, and within the same declarative part as the calling point. Finally, any
procedure can be called if it is prior to, on the same level, and within the same declarative part as any
subprogram within which the calling point is nested. In simpler words, a procedure is visible in three
cases. First, if it is within the declarative part of the calling procedure. The second case is if it is a
peer (on the same level within a parent subprogram) or thirdly, if it is a peer of any parent.
The procedure named Triple can therefore call Second_Layer, but not Bottom_Layer, since it is at
a lower level and is not visible. The main program, according to these rules, is only allowed to call
Triple, because the other two procedures are nested too deeply for a direct call. Be sure to compile
and run this program and study the results.
ADA FUNCTIONS
Example program ------> e_c08_p6.ada
The program named e_c08_p6.ada has two examples of Ada functions. A function differs from a
procedure in only two ways. A function returns a single value which is used in the place of its call,
and all formal parameters of a function must be of type in, with no other mode permitted. In the
program under consideration, two functions are illustrated, one beginning in line 13, and the other
beginning in line 18. Note that each begins with the reserved word function.
A FUNCTION SPECIFICATION
In a manner similar to that defined for a procedure we can define a function specification that gives
the interface of the function to any potential caller. You will find the function specification useful
later in your Ada programming efforts. It is similar to the Pascal forward declaration. Note once
again, that the function specification is not required in this case, it is only given here as an
illustration.
The function named Square requires one argument which it prefers to call Val, and which must be of
type INTEGER. It returns a value to the main program which will be of type INTEGER because
that is the type given between the reserved words return and is in the function header in line 13.
A function must return a value, and the value is returned by following the reserved word return with
the value to be returned. This return must be done in the executable part of the program, as illustrated
in line 15. It is an error to fail to execute a return statement and fall through the end of a function.
Such a runtime error will be reported by raising the exception Program_Error, which will be
explained later in this tutorial.
CAN YOU RETURN FROM A PROCEDURE?
It would be well to point out that you can return from a procedure by using the return statement also,
but no value can be given since a procedure does not return a value in the same manner as a function.
The return statement can be anyplace in the procedure or function and there can be multiple returns if
the logic dictates the possibility of returning from several different places.
A VALUE IS SUBSTITUTED FOR THE FUNCTION CALL
Examining the executable part of the program, we find that the variable Twelve has been initialized
to the value of 12, and is used in line 25 as the argument for the function Square. This causes Square
to be called, where the value of 12 is squared and the result is returned as 144. It is as if the resulting
value of 144 replaces the function call Square(Twelve) in the Put procedure call, and the value of
144 is displayed. Continuing on to line 27, the variable Twelve, which still contains the value of 12,
and the constant 12, are given to the function Sum_Of_Numbers which returns the sum of 24. This
value is assigned to the variable named Sum where it is stored for use in line 29.
A function can be defined with no input parameters, in which case, the function is called with no
parameters. Such a case would be useful for a random number generator where the call could be X :=
Random; assuming a new random number is returned each time the function is called. Compile and
execute this program and study the output generated.
A FULLER EXAMPLE
Example program ------> e_c08_p7.ada
Examine the program named e_c08_p7.ada which is a rather odd program that computes the square
of an integer type variable, but maintains the sign of the variable. In this program, the odd square of 3
is 9, and the odd square of -3 is -9. Its real purpose is to illustrate several procedures and a function
interacting.
The main program named OddSqre has a function and a procedure nested within its declarative part,
both of which have parameters passed. The nested procedure named Square_And_Keep_Sign has
another procedure nested within its declarative part, named Do_A_Negative_Number which calls
the function declared at the next higher level.
This program is a terrible example of how to solve the problem at hand but is an excellent example of
several interacting subprograms, and it would be profitable for you to spend enough time with it to
thoroughly understand what it does.
COMMENTS ON e_c08_p7.ada
This program illustrates some of the options that are purely programming taste. The first option is
illustrated in line 21, where we could have chosen to use the construct Number_To_Square**2
instead of the simple multiplication. Either form is correct and the one to be used should reflect the
nature of the problem at hand. The second option is the fact that three returns were included in lines
36, 39, and 42, when a single return could have been used following the end of the if statement. This
was done to illustrate multiple returns in use. In some cases, the logic of the program is much clearer
to use several returns instead of only one. More than anything else, it is a matter of personal taste. Be
sure to compile and execute this program.
OVERLOADING
Example program ------> e_c08_p8.ada
We have casually mentioned overloading earlier in this tutorial and it is now time to get a good
example of what overloading is by examining the program named e_c08_p8.ada. This program
includes two functions and two procedures and all four of these subprograms have the same name.
The Ada system has the ability to discern which subprogram you wish to use by the types included in
the actual parameter list and the type of the return. In line 45, we make a call to a function with a 2,
which is an integer type constant, and we assign the returned value to an INTEGER type variable.
The system will look for a function named Raise_To_Power with a single integer class formal
parameter and an INTEGER type return which it finds in lines 11 through 15, so it executes this
function. The actual searching will be done at compile time so the efficiency is not degraded in any
way by the overloaded names.
If you continue studying this program you will see how the system can find the correct subprogram
by comparing types used as formal parameters, and the type returned. Using the same name for
several uses is referred to as overloading the subprogram names and is an important concept in the
Ada language.
OVERLOADING CAN CAUSE YOU PROBLEMS
If we made an error in this example program, by inadvertently omitting the decimal point in line 47,
and assigning the result to an INTEGER type variable, the system would simply use the wrong
function and generate invalid data for us. An even worse problem could be found if we had a function
that used an INTEGER for input and a FLOAT for output, because only one small error could cause
erroneous results. Because of this, it would be to your advantage to use different subprogram names
for different operations, unless using the same name results in clear code.
In the case of the text output procedures which we have been using, it makes a lot of sense to
overload the output subprograms to avoid confusion. The name Put is used for outputting strings,
integers, enumerated types, etc, and we are not confused. Overloading can be an advantage in certain
cases but should not be abused just because it is available. Be sure to compile and execute this
program.
HOW ARE PARAMETERS PASSED TO A SUBPROGRAM?
If you understand the method of passing parameters to and from a subprogram, it may occasionally
be possible to improve the efficiency by selecting the type carefully. For that reason the following,
admittedly sketchy, descriptions are given;
G Parameters of scalar types are always passed by copy, and when the subprogram returns, the
new value is copied back to the original. Therefore, in the case of out or in out parameters,
intermediate values are not reflected back to the original until the subprogram is complete.
G Composite parameters (array and record) can use either a copy as defined above, or a
reference to the original. The compiler writer has the option of choosing the means of
parameter passing in this case.
G The more advanced types, such as task types or protected types, are always passed by a
reference to the original.
Generally, it shouldn't matter to you how the various types are passed, but it might matter in some
situations.
PROGRAMMING EXERCISES
1. Rewrite e_c05_p6.ada to do the temperature conversion in a procedure.(Solution)
2. Rewrite e_c05_p6.ada to do the temperature conversion in a function.(Solution)
3. As mentioned in the text, add a function to the program e_c08_p8.ada that uses an INTEGER
for input and returns a FLOAT type result. Remove the decimal point from line 47 to see that
the new function is called when we really intended to call the procedure with the floating point
number.(Solution)
Advance to Chapter 9
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 9
BLOCKS AND SCOPE OF VARIABLES
LARGE PROJECT DECOMPOSITION
Since Ada is a highly structured language, it has the means to divide a large project into many smaller
projects through use of procedures and functions. Because procedures and functions can be nested
within other procedures and functions, we have the problem of visibility and scope of types,
variables, constants, and subprograms.
WHAT IS THE SCOPE OF A VARIABLE?
Example program ------> e_c09_p1.ada
Examine the program named e_c09_p1.ada for several examples of variables with different scopes.
You should spend a few minutes familiarizing yourself with the structure of the program which
contains the main program, or procedure, and four procedures embedded within it. We will begin
with the variable named Count declared in line 4, and state that it has a scope which extends from the
semicolon at the end of its declaration to the end of the entire program in line 32. Its scope extends to
the end of the program because it is declared in the declaration part of the main program. It is
commonly referred to as a global variable.
WHERE IS A VARIABLE VISIBLE?
The variable named Count, declared in line 4, is visible anyplace in the range of its scope, except for
one small area of the program. Since another variable with the same name is defined in line 10, the
first one is effectively hidden from view within the range of the newer, local variable. Note that it is
the local variable that takes precedence and hides the global variable, rather than the other way. The
variable named Count from line 4, is not visible from the end of line 10 through the end of line 13. It
should be clear that the scope of the local variable extends to the end of the executable portion of the
subprogram in which it is declared.
In like manner, the variable named Index, defined in line 7, has a scope that extends from the end of
line 7 to the end of its procedure which ends in line 23. The variable named Index is visible
throughout its range, because there are no other variables of the same name in a lower level
subprogram. The variable named Data is visible throughout its range which extends from the end of
line 16 through the end of line 19.
THAT WAS ACTUALLY A LIE
The global variable Count is not visible from lines 10 through 13, but there is a way to use it in spite
of its hidden nature. This will be the topic of the next example program, but you should compile and
run the present program to see that it really will compile as given. There is no output, so execution
will be uninteresting.
USING THE DOT NOTATION
Example program ------> e_c09_p2.ada
Examine the program named e_c09_p2.ada for some examples of making an invisible variable
visible. The careful observer will notice that this is the structure of the last program with additional
variables declared, and some added assignment statements.
We will consider three variables of the same name, Count, and see that we can use all three variables
in a single statement if we so desire. Assume we are at line 12 in the program where we wish to use
the local variable named Count, the one that was declared in line 10. By the definition of Ada, the
innermost variable will take precedence and by simply using the name Count, we are using the
desired one. If however, we would like to use the one declared in line 4, we can do so by using the
"dot" notation illustrated in line 13. We are giving the compiler a complete map on where to find the
variable. The dot notation can be read as follows, "Go to the outer level, Scope2, dot, and the variable
named Count." Line 13 is therefore referring to the variable declared in line 4. Using the notation
Scope2.Level1.Count would refer to the variable declared in line 7.
Additional examples of the use of dot notation to use otherwise invisible variables are given in lines
21 through 30. This is also called the expanded name of the variable or the expanded naming
convention.
RENAMING A VARIABLE
In order to reduce the number of keystrokes used and to improve the clarity of some programs, Ada
provides a renaming capability. Line 18 illustrates this by renaming the triple component
combination to the much simpler name, Outer_Index. Anyplace in the procedure where it is
permissible to use the longer name, it is also legal to use the new shorter name, because they are
simply synonyms for the same actual variable. This is a construct that could easily be abused in a
program and make a program unnecessarily complicated, so it should be used sparingly.
Compile and run this program, even though it has no output, to assure yourself that it actually will
compile. The dot notation will be used in many other places in Ada, so you should become familiar
with it.
AN ADA BLOCK
Example program ------> e_c09_p3.ada
Examine the program named e_c09_p3.ada for an example of the use of an Ada block. Just as you
can define a procedure and jump to it, by calling it of course, 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
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. The example program contains two such blocks, the first in lines 17
through 27, and the second in lines 34 through 47. The only real difference is that the second block is
a named block, with the name Who, the use of which will be defined shortly.
Study the program and you will see that even though there are several variables defined in the block,
and at least one is a repeat of a global variable, all are actually available through use of the dot
notation defined during our study of the last program. In the first block, the local variables are the
default variables when there is a repeated name, but in the second block, the variables can be
specifically named by using the dot notation. This is possible because the block is named, the name
being Who in this particular case. The name is mentioned just prior to the block followed by a colon,
and the name is repeated following the end of the block. In this case, the name does nothing for you,
but if there were two nested blocks, either or both could be named, and you would be able to select
which variable you were interested in. There is no limit to the number of blocks that can be nested.
Note that the name used for a block is not a label to which you can jump in order to execute a goto
statement. The name is used only to name the block.
If no declarations are needed, you can declare a block without the reserved word declare, using only
the execution block delimiters begin and end. Without the declaration part, there is little reason to
declare the block until we come to the topic of exception handling where it will be extremely useful
to have this capability. Compile and execute this program and study the results. Be sure you
understand where each of the displayed values come from.
Examine section 5.6 of the Ada 95 Reference Manual (ARM) to gain a little more experience in
working with the ARM. You may be quite surprised at the brevity of this section about the block.
WHAT ARE AUTOMATIC VARIABLES?
Example program ------> e_c09_p4.ada
This is a good time to discuss a very important topic that can have a significant effect on how you
write some of your programs in the future. The topic is automatic variables, what they are and what
they do for you. The best way to define them is to examine another program, and the program named
e_c09_p4.ada is written just to illustrate this point.
The program is actually very simple since it is merely one big loop in which the variable Index
covers the range from 1 through 10. Each time we pass through the loop, the block in lines 16
through 26 is executed and contains another loop to output some integer type data. Take careful
notice of the constants and the way they are used, and you will see something that is a little strange.
Each time we enter the block, we enter with a larger value for the loop variable, in this case named
Index, and therefore the constants are different each time through the block. During each pass,
however, they are constant, and will be treated as such. This behavior is perfectly normal and will be
clearly understood when we define an automatic variable.
Prior to entering the block, the two constants, and the variable named Count_Stuff do not exist.
When the block is entered, the constants are generated by the system, initialized to their constant
values, and available for use within the block. Since the constants are generated each time the block
is entered, it is possible to initialize them to a different value each time, which is exactly what is
being done here. The process of assigning the constants their values is called elaboration.
The variable is also generated, and made available for use within the block where it is assigned a
nonsense value for illustrative purposes, then never used. When program control leaves the block, in
this case dropping out the bottom, the two constants and the variable disappear from existence
entirely and are no longer available for use anywhere in the program. They are said to have a limited
lifetime, their lifetime being from the time they are elaborated until we leave the block in which they
are declared. The scope and lifetime of a variable or constant is therefore very important for you to
understand.
It should be clear to you that the outer loop variable, Index, is visible from lines 12 through 29 except
for lines 23 through 25. Within the region of lines 23 through 25, the outer loop variable cannot be
used, even by using the expanded naming convention (i.e. - the dot notation), because there is no
name for the outer loop. Naming the outer loop would make the outer loop variable available.
WHERE ELSE IS THIS USED?
This concept of the automatic variable is very important because it is used in so many places
throughout an Ada program. It is used in four different places in the present example program, once
in the block, as we have just mentioned, once in the main program itself, where the four variables
with animal names are automatically generated, and twice in the for loops. The variables named
Index in each of the for loops are automatic variables that are generated when the loop is entered,
and discarded when the loop is completed. As you can see, there is a very good reason why the loop
control variable is not available after you leave the loop.
Each time you call a procedure or function, the formal parameters are generated, as are the defined
variables and constants. The process of variable generation and constant initialization is called
elaboration. They are then used within the subprograms, and discarded when the procedure or
function is completed and control is returned to the calling program.
Since the main program is itself a procedure, its variables are handled the same way.
THE STACK STORES THE AUTOMATIC ENTITIES
The generated constants and variables are stored on an internal stack, the definition of which is
beyond the scope of this tutorial. If you understand what a stack is, it should be clear to you how the
system can generate items, place them on the stack, use them, and discard them. Also, due to the
nature of a stack, it should be clear to you how additional variables can be placed on the stack as calls
are made to more deeply nested procedures and functions. Finally, it is only because of this use of
automatic variables that recursive subprograms can be used. Ada requires that all subprograms be re-
entrant and use of the stack makes this possible.
If the last paragraph is too technical for you, don't worry about it, because it is only mentioned for
general information, and is not needed to understand Ada programming.
Compile and run e_c09_p4.ada and study the output. Be sure you understand the concept of the
automatic variable because some of the advanced programming techniques in Ada will require this
knowledge.
PROGRAMMING EXERCISES
1. Modify e_c09_p3.ada to include a procedure in the declaration part of the first block. The
procedure should output a line of text to the monitor when called from the executable part of
the block. Why can't this procedure be called from outside of the block?(Solution)
2. Name the block in e_c09_p4.ada and output the value of the outer Index in the loop using the
dot notation. It will output the same number six times since the outer Index is invariant in the
inner loop.(Solution)
Advance to Chapter 10
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 10
ARRAYS
OUR FIRST ARRAY
Example program ------> e_c10_p1.ada
An array is a group of two or more elements that are all of the same type. In Ada, as in most modern
computer languages, arrays can be made of many different kinds of data, but all elements of an array
must be of the same type. The best way to see this is to inspect the program named e_c10_p1.ada
which contains a few examples of arrays.
HOW DO WE DECLARE A SUBSCRIPT?
For simplicity, we will start with line 8 where we have a declaration of the array named Dummy1.
This line says that the variable named Dummy1 will have 7 elements numbered from 1 through 7,
and each element will have the ability to store one BOOLEAN variable. We will see shortly that the
individual elements will be referred to by the variable name followed by a subscript in parentheses, or
Dummy1(1), Dummy1(2),... to Dummy1(7). Keep in mind that each is a single BOOLEAN
variable.
To define an array, we use the reserved words array and of with the appropriate modifiers as
illustrated in this example. We define a range which the array will cover, the type of the range
variable, which must be composed of discrete type limits, and the type of each element of the array.
Remember that a discrete type is any type of the integer class including enumerated types. We will
have an example program with an enumerated array index in part 2 of this tutorial.
LET'S LOOK AT ANOTHER ARRAY DECLARATION
In line 9, we have Dummy2 defined as an array of BOOLEAN type variables that covers the range
of Dummy2(-21) through Dummy2(10), since N has the value of 10. Line 10 illustrates declaring the
array Dummy3 as an array of BOOLEAN variables from Dummy3(-21) through Dummy3(10), but
this time the subscript type is not explicitly stated, only implied by the values of the subscript limits.
Actually, the subscript type was not needed in the first two either, since they were implied. The type
INTEGER is used so often for an array subscript that the type INTEGER has been defined as the
default if none is given. This is done only to make it a little simpler to use arrays.
So far in this program, we have defined about 70 variables that have no initial values because we
have not assigned them any. We will see how to initialize an array later, but first we will learn how to
use them.
HOW DO WE DEFINE AN ARRAY TYPE?
Line 12 gives the general method of declaring an array type. It uses the reserved word type followed
by the type name, the reserved word is, then the definition of the type. The definition is composed of
the range, followed by the reserved word of and the element type. We now have a type name which
can be used to define any number of array variables, and each will be made up of integer variables.
Each will also have 5 elements and will cover the range of 1 through 5. Thus line 14 defines an array
of five elements named Total, the elements of which will be named Total(1), Total(2), ... Total(5).
Each of these elements can be used to store one value of type INTEGER.
Line 22 of the program illustrates how we can assign a value of 12 to one of the elements of First,
which is another array of type MY_ARRAY and therefore composed of 5 elements. The second
element is assigned the value of 16, and the third is assigned a value found by subtracting the first
element from the second, resulting in a value of 4. This illustrates the use of the elements once they
are assigned values. The fourth and fifth elements are also assigned nonsense data, illustrating some
mathematical operations. The assignment in line 25 will be explained in the next paragraph. If you
remember that each element is an INTEGER type variable, you can use them just like any other
INTEGER type variable, except that you must add the subscript to indicate which element you are
interested in using at each point in the program.
RENAMING AN ARRAY ELEMENT
Line 19 is an illustration of renaming a single element of the array. Once again, this simply gives us a
synonym which we can use to refer to the variable, it does not declare another variable. It should be
pointed out that it is not permitted to rename a type but you can achieve the same effect by declaring
a subtype with the same range as the parent type.
WHAT IS A RANGE_ERROR?
Any attempt to use a subscript which is outside of the assigned range will result in the system raising
the exception Range_Error, and will be handled as a fatal error, terminating operation of the
program unless you handle the exception yourself. We will study exceptions in part 2 of this tutorial.
The subscript can be a variable itself, provided it is of the correct type, as is illustrated in line 29,
where all five elements of the array named Second are assigned nonsense data for illustration. The
subscript can also be calculated, with any arbitrary level of complexity, provided of course that it
results in an INTEGER type result and is within the range of the declared subscript.
ARRAY ASSIGNMENT
The assignment in line 32 is legal, provided the two arrays are of the same type, resulting in all 5
values of the array named First being assigned to the five elements of the array named Total. Line 33
illustrates that arrays can even be compared for equality or inequality, and they are considered equal
if each element of Total is equal to the corresponding element of First. If there is any inequality, the
result is FALSE.
Lines 38 through 45 illustrate a few more of the permissible operations on arrays. You should have
no trouble studying them on your own.
WHAT IS AN ANONYMOUS TYPE?
In line 12, we defined a type and gave it a name which we could then use at will throughout the
remainder of the program. Any array of this type is said to be of type MY_ARRAY, because it has a
name assigned to it. The array declared in line 17 does not have a name associated with it, so it is
referred to as an anonymous type.
The array assignment in line 32 was only possible because the two arrays were of the same exact
type, and in order to be of the same type, they must be declared with the same type name. The array
named Funny has the identical structure as the array First, but it was not declared with the same type
name, and it is therefore of a different type and cannot be used in an array assignment statement.
Since the array Funny is of an anonymous type, it is impossible to define another array with the same
type, so it is impossible to use this array in an assignment statement with any other array.
Line 18 declares the arrays X and Y in the same statement, and it would seem that they should have
assignment compatibility, but according to the definition of Ada, the two arrays are of different types
because naming both variable names in one statement is merely a shorthand method for naming them
in two separate lines. The two arrays are therefore each of a separate anonymous type. This is a fine
point, but should be clearly understood.
Two arrays are assignment compatible only if they are declared with the same type name. Compile
and run this program and compare the output with the output you expect.
WHAT IS A SLICE OF AN ARRAY?
Example program ------> e_c10_p2.ada
The program named e_c10_p2.ada contains several examples of the use of the slice in Ada, which is
a portion of an array. You may wish to assign part of an array to part of another array in a single
statement. This can be done with a slice.
We begin by declaring an array type, MY_ARRAY, which is then used to declare two arrays, First
and Second. Finally we declare a third array named Funny, which is of an anonymous type, which
we explained during our study of the last example program. The current example program will
illustrate the difficulty of working with an array of anonymous type, but we will start by working
with the named arrays.
WHAT IS A SLICE?
In the executable part of the program, we assign nonsense values to the arrays named Funny and
First, so we will have some data to work with. Then in line 22 we tell the system to take elements 3
through 7 of the array named First and assign them to elements 1 through 5 of the array named
Second. The term on each side of the assignment operator is a slice, a portion of an array. In order to
do the slice assignment as illustrated, both arrays must be of the same type and both slices must have
the same number of elements. Of course, all slice limits must be within the declared range limits of
the subscripts for the type in use. Line 23 illustrates copying 4 elements from First to Second, and
line 24 illustrates copying 6 elements.
In line 25, eight elements are copied from an array to itself in such a manner that the destination and
origin portions of the array overlap. Ada is defined such that all of the values are copied in true
fashion rather than recopying some earlier copied values again as the copying of values continues.
This is because the entire right hand expression is evaluated before the assignment is made to the left
side variable. Note that the slice can only be used with a singly dimensioned array.
BACK TO THE ANONYMOUS TYPE VARIABLE
We said that the variable named Funny is an anonymous type and that it would cause some
difficulties, so let's see what the problems are. In order to assign all or part of an array to another type
of array, we must use a type transformation or get a compile error. Line 27 illustrates how to use a
type transformation, as we have seen before. However, we can only do a type transformation based
on the entire array, not a portion of it, so we can only transform the type from anonymous to a full
sized array of the target type, MY_ARRAY. Therefore we can only copy a slice from the anonymous
type variable into the full array of the target. There is no way to transform the type from
MY_ARRAY to the anonymous type, since the anonymous type doesn't have a name, so a slice
cannot be used to assign to the array variable named Funny. Line 28 illustrates another slice
assignment to the complete array named First.
The entire array named First is displayed for your observation. Compile and run this program and
examine the output.
A MULTI DIMENSIONAL ARRAY
Example program ------> e_c10_p3.ada
Examine the program named e_c10_p3.ada for our first example of a multidimensional array. We
begin by declaring a type named MATRIX which is composed of an array of an array with a total of
12 elements. Each element of the array is referred to by two subscripts following the variable name in
parentheses as illustrated in the executable part of the program. Lines 18 through 23 contain a nested
loop to fill the variable named Square_Board with a multiplication table, and to fill Chess_Board
with all zeros. Note that the variable named Chess_Board is of an anonymous type because there is
no type name associated with it.
The entire array named Square_Board is assigned to the array Checker_Board in line 25, which is
legal because they are of the same type, which means they were defined with the same type name.
Line 27 is used to assign a value of 2 to one element of the Checker_Board, and that value is used in
line 28, which states, "Checker_Board(2,4) := 17;", because line 27 assigned the value of 2 to
Checker_Board(2,3).
It should be clear to you, based on the discussion of the last program, that even though Chess_Board
has the same structure as Square_Board, they are not type compatible and are not assignment
compatible. The individual elements are assignment compatible however.
The array named Checker_Board is displayed which you can observe when you compile and run
this program. By the way, the array names are poorly chosen in this example because a chess board is
not 3 by 4. Good naming conventions help toward developing quality software.
WE NEED SOME FLEXIBILITY
Example program ------> e_c10_p4.ada
The previous program used fixed values for all of the range and loop definitions, which allows very
little flexibility, and is therefore considered to be poor programming practice. Of course, it was done
for clarity since this was your first look at a multidimensional array. The next program, named
e_c10_p4.ada is much more flexible and illustrates some of the slightly more advanced techniques
which can be used with Ada.
This program is identical to the last except that there are two constants defined in the declaration part
which are then used to define the limits of the arrays and the loops. If you needed to make the
program cover a larger range, it would be trivial to modify the constants and recompile the program.
Compile and run this program and you will see that it does exactly the same thing as the last one.
WE NEED MORE FLEXIBILITY
Example program ------> e_c10_p5.ada
Examine the program named e_c10_p5.ada and you will find that it is identical to the last two except
in the way we define the loop limits. Recall the information we covered on attributes in an earlier
lesson and the additions to this program will be simple for you to understand. In line 21 we use the
attribute LAST to define the upper limit of the outer loop and add the digit "1" in parentheses to tell
the system that we are interested in the first subscript of the array named Square_Board. Line 22
uses a "2" to indicate the LAST value of the second subscript of Square_Board. If no subscript
indication is given, the system will default to "1", but it is much clearer for the reader to indicate the
"1" in parentheses. It may seem to you to be a lot of trouble to define the limits this way, but when we
get to the point where we are writing generalized procedures we will need the flexibility given here.
Generic procedures are a long way off too, but these techniques will be absolutely essential when we
study them.
Lines 34 and 35 also use attributes for the two loop ranges, but they use the RANGE attribute with
the number of the desired subscript in parentheses once again.
When using a singly subscripted array, it is legal to use a "1" in parentheses also, but if none is given,
the system will default to one. You should explicitly include the number in a multidimensional array
and omit it for a singly dimensioned array as a matter of programming clarity.
HOW DO WE INITIALIZE ARRAYS?
Example program ------> e_c10_p6.ada
Examine the program named e_c10_p6.ada for some examples of array initialization. Seven arrays
are declared and the method of aggregate notation, both positional and named, are illustrated. An
aggregate is a group of numeric literals, although enumeration values could also be included, that are
used in many places in Ada. We will use an aggregate to initialize an array in this example program.
The literals can be grouped in the order in which they are used, and this is referred to as a positional
aggregate. The literals can also be in a named aggregate, in which the use for each value is defined by
using the name of the location to which it should be assigned.
The variable Total is initialized using the positional notation and the variable First is initialized by
use of the named notation. Mixed notation is not allowed for array initialization. The array named
Another in line 13 contains a new construct using the reserved word others in conjunction with the
named aggregate notation. If included, it must be the last entry in the aggregate. The values of
Another(1), Another(3), and Another(4) will be initialized to the value of 3. The array named
One_More in line 15 illustrates initialization of a range of variables to the value 13, and two
variables to the value 27. Note that the others case can be included here but must be last and alone.
The other singly dimensioned arrays should pose no problem for you but a few comments are in order
concerning the multi dimensional arrays.
Even though you are not permitted to use mixed aggregate notation for an array, the rule is applied at
only one level so you can use different methods for each level.
The variable Square_Board uses all positional notation, but Checker_Board uses a named
aggregate for the first subscript and positional for the second. Chess_Board mixes things up a little
more by using a named aggregate for the first subscript and both methods for the second subscript
even though it is consistent within each subgroup and is therefore obeying the rules.
MORE ARRAY EXAMPLES LATER
There is more to be said about arrays, but it will have to wait until we cover a few more topics. This
is meant to get you started using arrays, but in a later chapter we will cover additional array topics.
PROGRAMMING EXERCISES
1. Modify e_c10_p1.ada in such a way that the variables X and Y are assignment compatible.
(Solution)
2. Write a program with two arrays of size 3 by 5 each and initialize each to a suitable set of
integer values. Multiply these, element by element, and store the values in a third array.
Finally, display the results in a clear format on the monitor.(Solution)
Advance to Chapter 11
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 11
THE CHARACTER AND STRING TYPE
A QUICK REVIEW OF THE CHARACTER TYPE
Example program ------> e_c11_p1.ada
The best way to study any topic is with an example, so examine the program named e_c11_p1.ada for
some examples using CHARACTER type variables.
The type CHARACTER is a predefined type in Ada and is defined as the printable set of ASCII
characters including a few that don't actually print. See Annex A.1 of the Ada 95 Reference Manual
(ARM) for a complete list of the CHARACTER elements. All of the operations available with the
enumerated type variable are available with the CHARACTER type variable. To illustrate their use,
we declare two CHARACTER type variables in lines 7 and 8 with the second being initialized to the
letter D. Note the single quote marks which define the CHARACTER type literal to which the
variable named Another is initialized. A different literal value is assigned to the variable My_Char
in line 12, and the two variables are compared in the if statement. Since 'A' is of lesser value than 'D',
the line of text in line 14 is output to the monitor.
Lines 17 through 20 display some very predictable output that is included as an example of
CHARACTER output, and finally some of the attributes available with the CHARACTER type
variable are illustrated in lines 22 through 24. The same attributes are defined for the CHARACTER
type variable as for the enumerated type and all are listed in Annex K of the ARM.
Compile and execute this program to get a feel for use of the CHARACTER type variable.
You may wish to review the program named e_c07_p3.ada in chapter 7 to refresh your mind on
declaring subtypes and derived types of the predefined CHARACTER type.
THE STRING TYPE
Example program ------> e_c11_p2.ada
The program named e_c11_p2.ada illustrates some of the operations that can be done with the
predefined type STRING. A string is an array of CHARACTER type variables which is of a fixed
length and starts with element number 1 or higher. The index uses type POSITIVE. Note that this
program is called e_c11_p2.ada instead of the more desirable name of STRING.ADA because the
word STRING is a predefined word in Ada and using it for the program name would make it
unavailable for its correct use.
Line 7 declares an uninitialized string of 33 characters, while line 8 declares a constant string of four
elements initialized to the word "John", and illustrates rather graphically that the string is composed
of individual CHARACTER type elements. Line 9 declares another constant that is initialized,
which all constants must be in order to be useful. Note that lines 8 and 9 did not contain a character
count, the computer counted the characters for us and supplied the limits of the array.
DECLARING A STRING VARIABLE
Line 10 defines a STRING variable, which will be initialized to the string given. Even though the
initialization string is given, the array limits must be explicitly specified for a variable. Not only must
the limits be given, the number of elements in the initialization string must agree with the number of
elements defined as the array range, or a compiler error will be given. This is the first difficulty
encountered when using strings, but there will be more as we progress. It seems like the computer
should be able to count the characters in the variable for us, but due to the strong type checking used
in Ada, this cannot be done.
STRING MANIPULATION IS DIFFICULT
When we get to the executable part of the program, we assign a string constant to the string variable
named Line. Once again, according to the definition of Ada, the string constant must have exactly the
same number of characters as the number of characters in the declaration of the variable Line, or a
compile error will be issued. This is another seemingly unnecessary inconvenience in the use of
strings which we must put up with. The variable named Line is displayed on the monitor in line 18,
and some of the other constants are displayed along with it. Note that the string literal in line 21 is
simply another string constant, but it does not have a name. Finally, we assign data to a few
individual elements of the string variable named Address in such a way to illustrate that it is indeed
an array, then do a slice assignment, and finally output the result. It should be noted that the
Put_Line could be used instead of the two separate output procedure calls in lines 30 and 31, but it is
simply a matter of personal taste.
Compile and run this program and see that the output is exactly what you predict from your
understanding of the program.
CONCATENATION OF STRINGS
Example program ------> e_c11_p3.ada
Examine the program e_c11_p3.ada for several examples of string concatenation. Two uninitialized
string variables are declared in lines 7 and 8, and they are used throughout the program.
Line 12 illustrates concatenation of a three element string and a four element string by using the
concatenation operator, the "&". The four element string is appended to the end of the three element
string forming a seven element string which is assigned to the variable String7. Line 21 illustrates
concatenation of a four element variable with a three element constant.
Line 24 is the most interesting assignment here, because it is a concatenation of four strings, two of
which contain only one element each. The values of "CR" and "LF" are such that they produce a
"carriage return" and "line feed" when sent to the monitor, so that when String7 is output, it will be
on two successive lines of the monitor. The ASCII values of all of the characters are available in the
predefined package named Ada.Characters.Latin_1, which is why the dotted notation gives the
actual value of these constants. Use of the dot notation in this manner will be more fully defined later
in this tutorial. Be sure to compile and run this program, and be sure you understand the results.
STRING COMPARISONS
Example program ------> e_c11_p4.ada
The example program named e_c11_p4.ada will give you some examples of string comparisons as
used in Ada, so you should examine it at this time. The string declarations are nothing new to you, so
nothing more will be said about them.
In line 15 where the constants MY_CAR and YOUR_CAR are compared for inequality, they will
not be equal since the case is different for some of the characters, and case matters in a string
expression. A different ASCII value is used for the letter 'A' than that used for the letter 'a', so they
are not the same. For a string comparison to be equal, all elements must be exactly the same as the
corresponding elements in the other string, and the number of elements must be the same. Therefore,
following execution of line 19, the value assigned to Her_Car is still not the same as the value stored
in the constant MY_CAR. If you attempted to compare them, you would get a compile error because
the two strings have a different length, so they could never compare anyway. Line 24 illustrates that a
variable can be compared to a string literal.
Lines 20 through 22 are examples of legal statements according to the Ada definition. Compile and
run this program and study the resulting output.
ATTRIBUTES OF CHARACTERS AND STRINGS
Example program ------> e_c11_p5.ada
Examine the program named e_c11_p5.ada for examples of how you can convert from
CHARACTER type variables to INTEGER type variables and back. The attributes POS and VAL
are used as shown. In order to increment a character, for example an 'A', to the next value, it must be
converted to INTEGER, incremented, then converted back to CHARACTER. Of course you could
always use the SUCC attribute to increment the CHARACTER type variable.
This program should be self explanatory. After you study it, compile and run it.
THERE ARE TWO KINDS OF STRINGS NOW
With the upgrade to Ada 95, there are now two kinds of strings. The STRING type that we have been
discussing in this chapter, and a new WIDE_STRING type. Since there are far more than 256
different characters in some languages around the world, and Ada is approved by the International
Standards Organization (ISO), it was necessary to provide the ability to handle many more
characters. The WIDE_CHARACTER type was defined which provides 65,536 different characters,
and the WIDE_STRING library was provided which uses the larger character type for each of its
characters. The first 256 characters of the WIDE_CHARACTER type are the same as the characters
in the CHARACTER type. The remaining characters can be defined as needed for whatever
language is being used in any given application.
A NEW, VERY USEFUL LIBRARY PACKAGE
Ada 95 has a new character handling library defined for use in text based processing. The library
named Ada.Characters.Handling is composed of many useful subprograms for use with text
handling. It contains, for example, a function named Is_Upper which returns a BOOLEAN value
indicating whether the character passed in as a parameter is upper case or not. Another function,
To_Upper changes the case of the character passed in to upper case, if it is an alphabetic character.
There are functions to check for whitespace, if a character is numeric, if it is a special character, and
many other useful functions. The student is encouraged to study this package provided by your
compiler, especially if text processing will be a major part of your programming efforts.
DYNAMIC STRINGS ARE COMING
You may not feel too good about the use of strings in Ada because of the lack of flexibility, but don't
worry about them. Ada was written to be an extendable language and when we get to chapter 16, we
will have an example package that will give you the ability to use strings the way you would like to.
A rather extensive dynamic string package will be presented to you and you will have the ability to
refine it even further if you so desire. In effect, you will have the ability to extend the Ada language.
Ada 95 has an improvement that was not available with Ada 83, the predefined string packages which
will be covered later in this tutorial.
PROGRAMMING EXERCISES
1. Write a program to declare your first name as a STRING constant, and your last name as
another STRING constant. Concatenate your first and last names with a blank between them
and display the result with a single Put_Line. You will find much inflexibility in the
definition of the STRING variable you use for the result.(Solution)
2. Add code to e_c11_p5.ada to increment the variable named Char by using the POS and VAL
attributes. Then add additional code to increment the same variable through use of the SUCC
attribute.(Solution)
Advance to Chapter 12
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 12
RECORDS
OUR FIRST LOOK AT A RECORD
Example program ------> e_c12_p1.ada
Ada has provision for two composite types, the array, which we studied earlier, and the record, which
is the topic of this chapter. Examine the program named e_c12_p1.ada for our first example of a
record.
Lines 7 through 12 declare an Ada record, which actually only declares a type. The usual syntax for a
type is used but when we come to the type definition itself, we begin with the reserved word record.
The components of the record are then inserted, and the record type definition is terminated by the
reserved words end record.
A record can contain any desired components, the only requirement being that the component types
must be declared prior to this definition, and the record type cannot be included as a component of
itself. The key point to keep in mind about records is, whereas an array is composed of some number
of like elements, the record is composed of some number of components that may be of different
types.
WHAT IS CONTAINED IN THE RECORD?
It is impossible to declare an anonymous record type like you can do in Pascal. The record must be a
named type prior to being used in a variable declaration.
In this record, we have a variable named Month that is permitted to store any value from 1 through
12, obviously representing the months of the year. There are also Day and Year variables, each of
which is different from Month since different constraints are placed upon each. After declaring the
record type, we still have no actual variables, only a type, but in lines 14 through 16, we declare four
variables of type DATE. Since the variables are of type DATE, each has three components, namely a
Month, Day, and Year. Notice that two of the variables are initialized to the values given in
parentheses in the order of the variable definitions. Month is therefore set to 5, Day to 25, and Year
to 1982 for each of the two initialized variables, Today and Pay_Day. The initialization will be very
clear after we discuss the program itself, so we will come back to it later.
HOW DO WE USE THE RECORDS?
Since Independence_Day is actually a variable composed of three different variables, we need a way
to tell the computer which subfield we are interested in using. We do this by combining the major
variable name and the subfield with a dot as shown in lines 19 through 21. This is called the selected
component notation in Ada. It should be clear to you that Independence_Day.Month is actually a
single variable capable of storing an INTEGER type number as long as it is in the range of 1 through
12. The three elements of the record are three simple variables that can be used in a program
wherever it is possible to use any other integer type variable. The three are grouped together for our
convenience because they define a date which we call Independence_Day. The data could be stored
in three simple variables and we could keep track of them in the way we usually handle data, but the
record allows a more convenient grouping and a few additional operations.
THE RECORD ASSIGNMENT
There is one big advantage to using a record and it is illustrated in line 23 where all three values
associated with the variable Independence_Day are assigned to the three corresponding components
of the variable Birth_Day. If they were separate variables, they would have to be copied one at a
time. The Day field of Pay_Day is assigned a new value in line 25 and the date contained in
Independence_Day is displayed on the monitor for illustrative purposes.
NAMED AND POSITIONAL AGGREGATES
Line 35 has an example of assignment using a named aggregate in which the three fields are defined
with their respective names and the pointing operator. It can be read as, "the variable named Day gets
the value of 19, Month gets the value of 2, and so on". Since they are named, they are not required to
be in the same order that they are in the record definition, but can be in any order. The real advantage
to using the named aggregate notation is the fact that all elements are named and it is clear just what
value is being assigned to each variable field. It should be pointed out that an aggregate is a group of
data which may or may not be of the same type.
Line 36 defines the three values of the record by simply giving the three values, but in this case, the
three elements must be in the correct order so the compiler will be able to assign them to their correct
subfields. This is called a positional aggregate. This is the kind of aggregate used to initialize the
dates in line 16.
A MIXED AGGREGATE
Line 37 illustrates use of a mixed aggregate in which some are defined by their position, and the rest
are defined by their names. The positional definitions must come first, and after a named variable is
given, the remainder must be named also. One point that must be remembered, all values must be
mentioned, even if some of them will not be changed. This seems like a picky nuisance, but it greatly
simplifies the compiler writer's job.
Compile and run this program, and you will get the date of Independence Day displayed on your
monitor. Be sure you understand this program, because understanding the next program requires that
you thoroughly understand this one. It should be clear that whether you use the named, positional, or
mixed notation, you are required to use the correct types for each of the parameters.
A RECORD CONTAINING A RECORD
Example program ------> e_c12_p2.ada
Examine the file named e_c12_p2.ada for an example of a record declaration containing another
record within it. We declare the record type DATE in exactly the same manner that we did in the last
program, but we go on to declare another record type named PERSON. You will note that the new
record is composed of four variables, one of the variables being of type DATE which contains three
variables itself. We have thus declared a record that contains three simple variables and a variable
record containing three more variables, leading to a total of six separate variables within this one
record type. In line 22, we declare three variables, each composed of six simple variables, so we have
18 declared variables to work with in our example program.
HOW TO USE THE COMPOSITE RECORD
Lines 28 through 30 should pose no real problem for you since we are using knowledge gained
during the last example program, but to assign the date requires another extension to our store of Ada
knowledge. Notice that in addition to the name of the main variable Self, we must mention the
Birth_Day variable which is part of it, and finally the subfield of the Birth_Day variable, Month in
line 31. The variable name is therefore composed of the three names, "dotted" together resulting in
the name of a unique simple variable. Once again, this is called the selected component notation.
Lines 32 through 34 assign the remaining three fields of the variable Self some meaningful data. Line
36 assigns all six elements of the variable Self to the variable Mother in one simple statement. Line
37 is used to assign the values of only the three components of Mother's Birth_Day to the three
corresponding components of Father's Birth_Day.
Since each subfield is actually a simple variable, each one can be used in computations as illustrated
in line 38 where Mother's Birth_Day Month is assigned the value which is 4 less than Self's
Birth_Day Month. This is only done to illustrate that the simple variables can be used in any way
you so desire, provided that you follow the rules of simple types.
RENAMING A RECORD COMPONENT
Line 24 illustrates how you can rename a component of a record in order to ease the problem of
entering the dotted notation each time a field is used. In this case the simple name My_Birth_Year is
a synonym for the extended naming required with all three components. Once again it must be
pointed out that this only affects speed of compilation since it is only an additional name which can
be used to refer to the variable. It must also be repeated that this facility should not be used except in
those few case where it really adds to the program clarity. Correct use of the new name is illustrated
in line 34.
RECORD ASSIGNMENT AND COMPARISON
As illustrated in lines 36, 37, and 41, entire records can be assigned to other records of the same type,
and entire records of the same type can be compared for equality or inequality. The records are equal
only if every subfield of one record is equal to the corresponding subfield of the other. The other
comparison operators are not available in Ada for records.
Compile and run this program even though you will not get any output. Add some output statements
yourself to see if you can get some of the data out to the monitor.
AN ARRAY WITHIN A RECORD
Example program ------> e_c12_p3.ada
Examine the file named e_c12_p3.ada and you will find an array type declared in line 17 which is
then used in the record type PERSON. The addition allows a variable of type PERSON to store four
grades giving us a little additional flexibility over the last program. The method of assigning data to
the new fields are illustrated in lines 38 through 41 and should require no additional comment,
because you are already versed on how to use arrays. One rule must be mentioned here, you are not
allowed to declare an array with an anonymous type within a record, it must be named. Be sure to
compile and run this program.
You will notice that lines 23 through 25 have default values assigned to some elements of the record.
The default values will be assigned to those elements every time a record of this type is declared. Of
course, the programmer can immediately override the default values by assigning any values he
desires, but the default values will be used to initialize those particular members.
AN ARRAY OF RECORDS
Example program ------> e_c12_p4.ada
Examine the file named e_c12_p4.ada for an example of an array of records. The types DATE and
PERSON are declared in a manner similar to their declaration in e_c12_p2.ada, but in line 26 we
declare an array of 35 variables, each of type PERSON, so each is composed of six variable fields. In
lines 46 through 52, we assign some nonsense data to each field of the 35 variables by using a loop.
Finally, we assign nonsense data to a few of the variables to illustrate how it can be done in lines 54
through 59. You should have no problem understanding this program.
Note that we could have assigned data to one of the records, the first for instance, then used it in a
loop to assign values to all of the others by using a record assignment such as, "Class_Member
(Index) := Class_Member(1);", and looping from 2 to 35. In a useful program, the data to be assigned
will be coming from a file somewhere, as we would probably be filling a database. This is, in fact, the
beginning of a very crude database.
Another new construct is illustrated in lines 27 and 28 where we initialize the variable named
Standard to the aggregate given. Note that, like the unnested record requirement, a nested record
must be initialized with an aggregate which includes all values. Lines 30 through 33 illustrate a
method of defining a null record which will be useful when we study inheritance later in this tutorial.
Compile and run this program to assure yourself that it really will compile and run.
It would be good for you to examine section 3.8 of the ARM at this time to become familiar with its
language and the method of definition used there.
THE VARIANT RECORD
The variant record is available with Ada 95 and it will be covered in detail in chapter 20, but it is
largely superseded by the more powerful techniques of inheritance and type extension which are
available with Ada 95.
PROGRAMMING EXERCISES
1. Rewrite e_c12_p4.ada and change the initialization aggregate for Standard from a positional
aggregate to a named aggregate.(Solution)
2. Rewrite e_c12_p1.ada to utilize the enumerated type for the month field as used in e_c12_p3.
ada. Note that you will need to instantiate a copy of the package named Enumerated_IO to
display the month name.(Solution)
Advance to Chapter 13
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 13
THE ACCESS TYPE VARIABLE
THE ACCESS TYPE IS DIFFERENT
Example program ------> e_c13_p1.ada
The access type variable is different from every other type we have encountered because it is not
actually a variable which can store a piece of data, but contains the address of another piece of data
which can be manipulated. As always the best teacher is an example, so examine the file named
e_c13_p1.ada for an example program with a few access type variables in it.
DECLARING AN ACCESS TYPE VARIABLE
In line 7 we declare a new type, an access type. As with all types, the reserved word type is given,
followed by the type name, then the reserved words is and the type definition. The type definition
begins with the reserved word access, which denotes an access type variable, then by the type which
we wish to access. The type which we wish to access can be any type which has been declared prior
to this point in the program, either a predeclared type or a type we have declared. Because there are
no predeclared access types available in Ada, we must declare all access types we wish to use.
The new type is used to
declare three access
variables in line 8. No
actual variables are
declared, only access to
three places in memory
which actually do not
exist yet. The access type
variables do not store an
integer value, as might be
expected, but instead store the address of an integer value located somewhere within the address
space of the computer memory. Note that the three access variables are automatically initialized to
null when they are declared, as are all access variables in Ada.
Figure 13-1 illustrates graphically the condition of the system at this point. A box with a dot in the
center depicts an access variable and an empty box will be used to depict a scalar variable.
WE NEED SOME DATA TO POINT AT
As we begin the executable part of the program, we have no data to access, so we create some data
storage in line 11 using the new reserved word. This tells the system to go somewhere and create a
variable of type INTEGER, with no name, and cause the access variable named Index to point at
this new variable. The effective address of the new variable is assigned to the access variable Index,
but the new variable still has no assigned value.
Line 12 tells the system to
assign the value of 13 to
the new variable by using
a very strange looking
method of doing so. For
the first two example
programs in this chapter,
we will simply say that
the value of all of the
variable is set to the
indicated value, namely 13. The end result is that the access variable named Index is pointing
someplace in memory which has no name, but contains the value of 13. Figure 13-2 illustrates our
current situation.
Lines 13 through 15 indicate that it is possible to display the value of this variable using the same
method we have used in all earlier lessons of this tutorial. If you remember to add the .all to the
access variable, you will be referring to the data stored at the location which it accesses.
Line 17 is used to create another variable of type INTEGER somewhere in memory, with Arrow
pointing to it, but which contains no value as yet. The next line says to take the value that is stored at
the location to which Index points, add 16 to it, and store the result, which should be 29, in the
location to which Arrow points. Arrow actually "accesses" the variable, but it is a bit more
descriptive to use the word points, especially if you are a Pascal or C programmer.
THE THIRD ACCESS VARIABLE
We have not yet used the access variable named There, so we instruct the system, in line 19, to cause
it to point to the same piece of data which Arrow is currently accessing. By failing to add the all to
the two access variables, we are assigning the access address to There rather than the value which
Arrow accesses. If only one of them had the all appended in line 19, there would be a type clash
resulting in a compiler error message. All three access variables are accessing some data somewhere,
so we can use their .all notation to display all three values. See figure 13-3 for a graphic
representation of the current data space.
Note that there are
actually only two
variables, because two of
the access variables are
pointing at the same piece
of data. This is illustrated
when one of the variables
is changed in line 26, and
when the data is printed,
it is evident that two of
the values are different.
Be sure to compile and run this program. When you think you understand it, see if you can modify it
such that all three access variables point to the same piece of data.
ACCESSING INTEGER AND FLOAT TYPE VARIABLES
Example program ------> e_c13_p2.ada
Examine the file named e_c13_p2.ada for some additional examples of access type variables. We
begin by declaring two access variables which access INTEGER type variables and three access
variables that access FLOAT type variables. It should be pointed out, and it probably comes as no
surprise to you, that it is illegal to attempt to access a variable with the wrong type of access variable.
Explicit type conversion is possible concerning the data types, but not the access types.
Line 14 introduces a new construct, that of initializing a variable when it is created. Using a form
similar to qualification, an INTEGER type variable is created somewhere in memory, initialized to
173, and the access variable named Index is assigned its address so that it points to it, or accesses it.
After executing line 15, the data space is as shown in figure 13-4.
Be sure to note the
difference between the
expressions in lines 21
and 22. In line 21, the
value stored at the place
where Index points, is
stored at the place
where Arrow points.
However, in line 22, the
access variable Index is
caused to point to the
same location where the access variable Arrow points.
A FLOAT TYPE ACCESS VARIABLE
Line 24 illustrates creation of a FLOAT type variable initialized with the value of Pi. Since the
access variable names are used in lines 25 and 26 without the all appended, all three FLOAT type
access variables are assigned to access the same variable, and some results are displayed. Figure 13-5
illustrates the condition of the system at this point.

The single FLOAT type variable is doubled in line 33, and it is displayed again three different ways.
Be sure to compile and execute this program.
ACCESSING A RECORD VARIABLE
Example program ------> e_c13_p3.ada
Examine the example program named e_c13_p3.ada for some additional uses for access variables.
This program begins by defining a record type, then an access type which can be used to access data
of this record type. Ignore the procedure Free in line 16 for a short time. In line 19, we declare a
variable named Myself which is an access variable that accesses a variable of the type
MY_RECORD. Since the record does not exist yet, the access variable is actually pointing nowhere.
According to the Ada definition, the created access variable will be initialized to the value null,
which means it points nowhere. This value can be tested for some value as we shall see later. All
access variables used in an Ada program, regardless of how they are declared, will be initially
assigned the value of null. This is true, unless they are specifically initialized to some value as shown
in line 20.

Line 20 is very interesting because we declare an access variable named Friend, and initialize the
access variable by creating a new record somewhere in memory, then initialize the record itself to the
values given in the positional aggregate. Finally, the access variable Friend is caused to point to the
newly created record. It is permissible to create a new record, but omit the initialization, supplying
the initial values in the executable part of the program as we are doing with the variable named
Myself. We finally declare a BOOLEAN type variable for later use. Figure 13-6 illustrates our
current data space.
USING THE RECORD ACCESS VARIABLE
In line 26, we create a new variable of type MY_RECORD, which is composed of three separate
fields. The three fields are assigned in much the same manner that they were assigned in the chapter
where we studied records, so this should pose no problem for you. In line 32, we create another new
record somewhere and initialize it to the values given, and cause the variable named Friend to point
to it, or access it. See figure 13-7.

NOW WE HAVE SOME LOST VARIABLES
Consider that the access variable Friend already had some data that it was pointing at, and we told
the system to cause it to point at this newly created record. The record it was formerly pointing at is
now somewhere in memory, but has nothing pointing at it, so it is in effect lost. We cannot store
anything in it, nor can we read out the data that is stored in it. Of even more consequence, we cannot
free up those memory locations for further use, so the space is totally lost to our program. Of course,
the operating system will take care of cleaning up all of the lost variables when our program
terminates, so the data is not lost forever. It is up to us to see that we do not lose memory space
through clumsy programming because Ada cannot check to see that we have reassigned an access
variable.
The next interesting thing is illustrated in line 40 where all of the fields of the record which Myself
accesses are assigned to all of the fields of the record which Friend accesses. Now it makes sense
why the designers of Ada chose to refer to the data which is accessed by the .all notation, it refers to
all of the data that the access variable points to. It should be pointed out that as you gain experience
with Ada you will find that nearly all access type variables are used to access records, and few, if
any, will access scalar variables.
BOOLEAN OPERATIONS WITH ACCESS VARIABLES
Records accessed by access variables can be compared for equality or inequality, just like regular
records, and they are equal only if all fields in one record are equal to the corresponding fields in the
other record. Line 42 will therefore result in TRUE, because the records are identical, due to the
assignment in line 40. Access variables can also be compared to each other, and result in TRUE only
if they are both pointing to the same object. In line 43, the result is FALSE because they are pointing
to different records, even though the records they point to happen to be equal to each other.
WHAT IS UNCHECKED DEALLOCATION?
The procedure Unchecked_Deallocation is a required part of Ada, so your compiler writer has
supplied you with this procedure as a part of the library. Any dynamically allocated data can be freed
up for reuse by the system through use of this procedure as illustrated in this program. You must first
instantiate a copy of the generic procedure as illustrated in line 16, and name it any available
identifier you choose. You must supply two types as parameters, the first being the object type you
wish to deallocate, and the second being the access type. The name Free is generally used because
that name is used for the equivalent procedure in Pascal and in C.
To actually deallocate some storage, you use the name of the access variable which is accessing the
storage to be released as the only parameter of the procedure named Free as illustrated in lines 46
and 47. This storage is then available for reuse by some other part of the program.
There is a lot more to be said about deallocation of storage and the way it is accomplished, but the
details will be left until chapter 23, after you gain more experience with Ada. Until then, with your
limited knowledge of Ada, you will probably not be writing programs in which you will need this
information. Be sure to compile and execute this program.
GARBAGE COLLECTION
Another way to deallocate the data accessed by the access variables, is to assign the value of null to
the access variables. This will cause the dynamically allocated variables to have no access variables
accessing them, and they are then unusable, or garbage. An Ada implementation may implement a
garbage collector to search for un-accessed data and reclaim the storage for further use.
Implementation of a garbage collector is optional according to the ARM. Much more will be said
about deallocation and garbage collection in chapter 25.
ACCESSING AN ARRAY OF DATA
Example program ------> e_c13_p4.ada
The example program named e_c13_p4.ada gives an example of using an access variable to access an
array. The only thing that could be considered new here is the assignment in line 20 where the value
of the array List_Of_Stuff, is assigned to the variable which is accessed by the access variable
There. Note that 6 INTEGER type values are actually assigned in this one statement.
Note that Unchecked_Deallocation is illustrated here also as an example. The program should be
simple for you to follow, and after you understand it, compile and execute it.
AN ARRAY OF ACCESS VARIABLES
Example program ------> e_c13_p5.ada
Examine the program e_c13_p5.ada for an example including an array of access variables which is
declared in line 17. The variable named Class is composed of a total of ten access type variables, any
of which can be used to point to a variable of type MY_RECORD. The loop in lines 26 through 29 is
used to first create a record variable, then assign values to the fields of the created record variable, for
each of the ten access variables. Since each record is composed of three subfields, a total of 30
separate variables are created and assigned values in this loop. A few of the variables are reassigned
values in lines 31 through 37 for illustrative purposes, and the entire array named Class is displayed
on the monitor.
Note that the array declared in line 17 is an anonymous type array.
Compile and run this program, and be sure you understand the resulting printout. The access variable
will be very important when we study some of the advanced programming techniques later in this
tutorial.
ACCESS TO STATIC OBJECTS
Example program ------> e_c13_p6.ada
Ada 95 has the ability to access a local or global variable with an access variable. This was not
permitted with Ada 83. In order to do so, it is necessary to identify the access variable as one with the
ability to access a local or global variable by using the keyword all as is done in lines 13 through 15
of the example program. In addition, the variables to be accessed must be identified as accessible
with the new keyword aliased as illustrated in lines 18, 21, and 24 of this program. This is a practice
that could be easily abused, so the designers of Ada force you to identify the access variable and the
variable to be accessed prior to actually doing the operation. This forces you to think carefully about
what you are doing. The use and abuse of pointers is the weakest spot in some other languages and
lead to very difficult to find errors.
You will note that the Ada compiler will complain, via an error, if any of the ingredients is missing
while using this technique. Lines 30 through 32 are included in this program to illustrate the various
rules of usage.
There is one other rule that must be followed and is checked by the compiler anytime you use an
access variable to refer to a local or global variable. The object referred to must exist for as long or
longer than the access variable itself, or the compiler will issue an error message. This is to prevent
the error of using an access variable when the object it refers to is no longer in existence.
ACCESS TO SUBPROGRAMS
Example program ------> e_c13_p7.ada
The example program named e_c13_p7.ada is an example of using a single access variable to call
three different functions. There are three functions defined in lines 7 through 20, and each have the
same number of parameters, the same type of parameters and the same return type. With all of this
parallelism, they are candidates for use with an access variable. In line 22 we define a type which
happens to have the same signature as the three functions listed above it, and finally we declare an
access variable of the new type named Multiply. In the executable part of the program we call the
three functions with the access variable in lines 31, 36, and 41. The same call results in calls to
different physical functions since it is pointing to a different function each time we call it. Lines 29,
34, and 39 are where we actually assign the address to the access variable.
This same technique can be used with procedure calls as well, provided that the signature for each of
the entities is identical. In the case of the procedure, all of the parameters must have the same mode.
They are by definition, all of the in mode in a function.
There is one other rule that must be followed and is checked by the compiler anytime you use an
access variable to refer to a subprogram. The object referred to must exist for as long or longer than
the access variable itself, or the compiler will issue an error message. This is to prevent the use of an
access variable when the object it refers to is no longer in existence, an obvious error. In those cases
when you think it is all right to override this rule, Unchecked_Access is available for use and
documented in the ARM.
The use of anything that is unchecked is highly discouraged.
PROGRAMMING EXERCISES
1. Declare a record named BOX_TYPE composed of three FLOAT type elements, Length,
Width, and Height. Use the new function to create three boxes named Small, Medium, and
Large, and store suitable values in all nine fields. Display the data concerning the three boxes
along with the volume of each box.(Solution)
2. Add the Unchecked_Deallocation procedure to the other three example programs, e_c13_p1.
ada, e_c13_p2.ada, and e_c13_p5.ada, and deallocate all allocated variables.(Solution 1)
(Solution 2)(Solution 3)
Advance to Chapter 14
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 14
TEXT INPUT/OUTPUT
ADA IS NOT BIG ON I/O
Ada was originally designed as a language for embedded real-time systems rather than as a business
oriented language, so input and output of data is a rather low priority part of the language. It may
surprise you to know that there are no input or output instructions available for use within the Ada
language itself, the problem being left to the compiler writers. However, the designers of Ada
realized that if the entire topic of input and output were left to the individual compiler writers, users
would be left with a mess trying to use nonstandard methods of input and output. They exercised
tremendous foresight by defining a standard external library to be used for input and output of data
that must be available with all Ada compilers that pass the validation suite.
Even though the libraries are defined, the details of their actions are not precisely defined, so there is
still some chance that your compiler will not behave in exactly the same way as the descriptions
given here. You will have to check the documentation packaged with your compiler to discover the
exact details of input and output operation. You may rest assured however, that the differences will
only be in the smallest details.
PACKAGE INSTANTIATION
Before we look at some text output procedures, we need to just scratch the surface on another topic.
We mentioned package instantiation several chapters back and we have reached a point where we
should define what that is to some extent. Ada provides the ability to write generic packages which
can be used to provide the same functionality for several different types without rewriting the code. A
generic package does not have a type associated with it, so we tell the system we would like it to
work with a certain type by instantiating a copy of the generic package with our particular type. If we
have, for example, 4 different types we need to work with, we instantiate a copy for each of the four
types and we can then perform the same operations on variables of each of those types. An
instantiation is a fancy way of saying we make an "instance" of the generic package. This will all be
covered in detail in part 2 of this Ada tutorial.
The Ada 95 environment provides a few of the generic packages pre-instantiated for us. For example,
a copy of the generic package Ada.Text_IO.Integer_IO is preinstantiated for the type INTEGER
and named Ada.Integer_Text_IO which we have been using in this tutorial. Likewise Ada.
Float_Text_IO is an instantiation of the generic package named Ada.Text_IO.Float_IO for use
with the type FLOAT. Now we have another problem since we need to define where the generic
packages mentioned in this paragraph came from. The best answer is that they came with our
compiler, and we will simply use them as illustrated until we study generic packages in part 2 of this
tutorial.
There are probably other pre-instantiated packages provided for us by our compiler writers. The types
INTEGER and FLOAT are required to be provided by every Ada compiler, but there other types
that can be provided. For example, type LONG_FLOAT may be provided as well as
SHORT_FLOAT, each with its own characteristics, and each with its own input/output package
named Ada.Long_Float_Text_IO or Ada.Short_Float_Text_IO. Other types may be
SHORT_INTEGER, SHORT_SHORT_INTEGER, or other variations of INTEGER, with the
packages Ada.Short_Integer_Text_IO or Ada.Short_Short_Integer_Text_IO. You should spend
some time studying the capabilities and limitations of your compiler to see just what types are
available for your use.
You will notice that many of these packages start with Ada. tacked onto the front of the full name.
Ada 95 includes the ability to define and use hierarchical libraries and many of the Ada 95 packages
are declared as a part of the Ada library. We will cover libraries in more detail later along with the
reasons for packaging them in this way.
Following that brief excursion, let's look at some text output procedures.
FORMATTED OUTPUT DATA
Example program ------> e_c14_p1.ada
If you examine the program e_c14_p1.ada, you will find that it does very little other than illustrate
the various methods of outputting formatted data to the monitor. Several types and variables are
declared, then six separate instantiations of Ada.Text_IO are declared for use with the six types used
in the program. Actually, there is nothing here that is new to you, since we have covered all of these
statements before, but they are included here in order to present an example of all of them in one
place.
A few comments should be made at this point. Note the six instantiations of Ada.Text_IO packages
given in lines 19 through 31. These are necessary in order to output the various types we are using in
this program. The package instantiations must be declared after the various types are declared. One of
the instantiations could be eliminated by transforming the type of the MY_INTEGER type variable
to type INTEGER prior to outputting it, but this is done for illustration. When we instantiate a
package, we are actually using a copy of a generic package, which will be the subject of a detailed
study in part 2 of this tutorial.
After you have studied the program and understand it, compile and run it for an elaborate set of
formatted output examples. Your compiler may output the float and fixed outputs slightly differently
than that given in the result of execution due to different defaults assigned by your compiler.
FORMATTED OUTPUT TO A FILE
Example program ------> e_c14_p2.ada
The example file named e_c14_p2.ada contains examples of how to use a file in a very easy fashion.
Since there is no standard method which is used by all operating systems to name files, we cannot
depend on what the rules will be in order to use Ada on any system, so we need two file names in
order to write to or read from a file. We need an internal name, which will be used by our program to
refer to the file, and which follows the naming rules defined by Ada. We also need an external name
which will be used by the operating system to refer to the file and whose name follows the rules
dictated by the operating system. In addition to having the two names, we need a way to associate the
names with each other, and tell the system that we wish to use the file so named. We do all of this
with the Create procedure as shown in line 11 of this program.
The variable Turkey, is the internal file name which we declared in line 7 to be of type
FILE_TYPE, a type exported by the package Ada.Text_IO for just this purpose. The "with Ada.
Text_IO;" statement in line 2 makes the type FILE_TYPE available in this program. The last
argument, which is either a string constant, as it is in this case, or a string variable, gives the external
name of the file we wish to create and write to. Note carefully, that if you have a file named TEST.
TXT in your default directory, it will be erased when this program is executed, because this statement
will erase it. The second parameter in this procedure call, is either In_File if you intend to read from
the file, or Out_File if you plan to write to it. There is no mode using text I/O in which you can both
read from and write to the same file. In this case, we wish to write to the file, so we use the mode
Out_File and create the file.
WRITING TO THE FILE
Line 13 looks rather familiar to us, since it is our old friend Put_Line with an extra parameter prior
to the text we wish to output. Since Turkey is of type FILE_TYPE, the system is smart enough to
know that this string will be output to the file with the internal name of Turkey, and the external
name of TEST.TXT, so the string will be directed there along with the "carriage return/line feed".
The next line will also be directed there, as will the 2 New_Lines requested in line 15. Line 16 does
not have the filename of Turkey as a parameter, so it will be directed to the default output device, the
monitor, in the same manner that it has been for all of this tutorial.
REDIRECTING THE OUTPUT
Line 18 contains a call to the procedure Set_Output which will make the filename which is given as
the actual parameter the default filename. As long as this is in effect, any output without a filename
will be directed to the file with the internal filename Turkey. In order to output some data to the
monitor, it can still be done, but it must be directed there by using its internal filename as a
parameter, the internal filename being Standard_Output. The group of statements given in lines 19
through 22 do essentially the same thing as those in lines 13 through 16. The default is returned to the
standard output device in line 23, and the program completes normally.
CLOSING THE FILE
The simple statement given in line 26 is used to close the file when we are finished with it. The
system only needs to be given the internal name of the file, since it knows the corresponding external
filename. Failure to close the file will do no harm in such a simple program, because the operating
system will close it automatically, but in a large program, it would be wise to close a file when it is
no longer needed. There is probably an upper limit on how many files can be open at one time, and
there is no sense wasting a file allocation on an unneeded file.
Be sure to compile and run this program, then check your default directory to see that you have the
file named TEST.TXT in it containing the expected text.
MULTIPLE FILES OPENED AT ONE TIME
Example program ------> e_c14_p3.ada
Examine the file e_c14_p3.ada for an example program in which we open and use 3 different files
plus the monitor. The three internal filenames are declared in line 7, and all three are opened in the
output mode with three different external names as shown in lines 12, 13, and 14.
We execute the loop in lines 16 through 25, where we write nonsense data to the three files, output a
test string to the display in line 27, and finally close all three files. Note that we could have defined
one of the files to be the default file and written to it without the filename, then used the filename for
the standard output in line 27, achieving the same result.
The program is fairly simple, so after you feel you understand it, compile and run it. After a
successful run, examine the default directory to see that the three files exist and contain the expected
results. Do not erase the generated files, because we will use them as example input files in the next
few programs.
INPUTTING CHARACTERS FROM A FILE
Example program ------> e_c14_p4.ada
Examine the file e_c14_p4.ada for an example of how to read CHARACTER type data from a file.
In this example the file name My_File is declared as a FILE_TYPE variable, then used to open
CHARACTS.TXT for reading since the In_File mode is used. In this case, the file must exist, or an
exception will be raised. The file pointer is set to the beginning of the file by the Open procedure so
we will read the first character in the file the first time we read from the file.
The loop in lines 14 through 18 causes us to read a character from the file and display it on the
monitor each time we pass through the loop. The function End_Of_File returns a TRUE when the
next character to be read is the end of file character for your particular implementation. We do not
need to know what the end of file character is, it is up to the compiler writer to find out what it is and
return a value of TRUE when the system finds it. The entire file is therefore displayed on the monitor
by this program, but with one slight deviation from what you would expect. Because of the way Ada
is defined, the end of line characters are simply ignored by the Get procedure, and since they are not
read in, they cannot be output to the monitor either. The end result is that the characters are all output
in one long string with no line feeds. You will see this later when you compile and run the program.
We will continue our study in this program and see a much better way to read and display the data.
THE RESET PROCEDURE
The Reset procedure in line 21 resets the file named as a parameter, which simply means that the file
pointer is moved to the first character once again. The file is ready to be read in another time. The
loop in lines 23 through 32 is the same as the previous loop except for the addition of another new
function. When the end of a line is found, the function End_Of_Line returns a TRUE, and the
program displays the special little message in line 27. In addition to the message, the New_Line
procedure is called to supply the otherwise missing end of line.
Unfortunately, we still have a problem because the End_Of_Line is actually a lookahead function
and becomes TRUE when the next character in the buffer is an end of line character. When the
program is executed, we find that it does not output the last character of each line in this loop,
because we never execute the else clause in the if statement for that character. When the program is
executed, you will notice that the periods at the end of the sentences are not displayed. The next loop
in this program will illustrate how to input and output the data correctly considering all of the Ada
idiosyncrasies.
If you recall, we stated at the beginning of this lesson that input and output programming in Ada was
at best a compromise. Don't worry, you will be able to input or output anything you wish to very
soon.
A CORRECT CHARACTER INPUT/OUTPUT LOOP
The file is once again reset in line 35, and a third loop in lines 38 through 46 reads and displays the
file on the monitor. This time when we go through the loop, we recognize that the end of line is
reading one character ahead in the input file, not the one we just read, and we adjust the program
accordingly. We output the character, then check for end of line, and call the New_Line procedure if
we detect an end of line. You will see, when you run it, that the last character on the line will be
displayed as desired.
It should be pointed out to you that the End_Of_File is another lookahead function and must be
tested after we output the last character read in. This loop does just that, because the end of file is
acted on after the character is output.
Be sure to compile and run this program. It would be interesting to edit the input file, CHARACTS.
TXT, adding a few characters, then rerunning the program to see how it handles the new data.
STRING INPUT AND OUTPUT
Example program ------> e_c14_p5.ada
The next example program, e_c14_p5.ada, illustrates how to read a string from an input file. In much
the same manner as for the character, the end of line character is ignored when encountered in the
input file. The result is that when the first loop is run, no "carriage returns" are issued and the text is
all run together. The second loop, however, uses the lookahead method and calls the New_Line
procedure when it detects the end of line in the input stream.
The big difference in this program and the last is the fact that a STRING type variable is used for
input here and a CHARACTER type variable was used in the last one.
I PLAYED A TRICK ON YOU
This program has a bit of a trick in it. The input file contains an exact multiple of 10 characters in
each line, and the input variable, of type STRING, has ten characters in it. There is therefore no real
problem in reading into the ten character string, but if the lines were not exact multiples of ten
characters, we would have gotten an input error. If you remember how difficult the STRING variable
was to work with when we studied strings, you will understand that we have the same problem here.
When we get to chapter 16 with its example programs, we will see how we can greatly improve the
string capability with an add-on library of dynamic string routines. Also, Ada 95 has several dynamic
string packages available for your use.
Be sure to compile and run this program, then add a few characters to some of the lines in the file
named CHARACTS.TXT to see the result of trying to read in an odd group of extra characters.
NOW TO READ IN SOME NUMBERS
Example program ------> e_c14_p6.ada
Computers are very good at working with numbers, and in order to work with them, we must have a
way to get them into the computer. The example file e_c14_p6.ada illustrates how to read INTEGER
type data into the computer from a file. Much like the last two programs, we open a file for reading,
and begin executing a loop where we read an INTEGER type number as input each time through the
loop. We are reading an INTEGER type number because that is the type of Index, the second actual
parameter in the Get procedure call. Since it is an INTEGER type number, the system will begin
scanning until it finds a valid INTEGER type number terminated by a space. It will then return that
number, in the computer's internal format, assigned to the variable we supplied to it, and make it
available for our use like any other INTEGER type variable. If any data other than INTEGER type
data were to be encountered in the input file before it found a valid INTEGER value, an exception
would be raised, and the program would be terminated.
The loop continues in much the same manner as the last two programs until it detects the end of file,
at which time it stops. There is one other slight difference when using numeric inputs, the system
does not read over the end of line character, because it doesn't know how to get across it. It gets stuck
trying to read the next data point but never gets to it because the end of line is blocking it. When the
end of line is detected, or at any other time, you can issue the function Skip_Line which tells it to go
to the beginning of the next line for its next input data. At any time then, you can begin reading on
the next line for your next data point.
Be sure to compile and run this program, then change the spacing of some of the numbers in the input
file to see that they are read in with a free form spacing.
WHAT ABOUT FLOATING POINT DATA INPUTS?
Once you understand the method of reading INTEGER type data in, you can read any other types in
by using the same methods. Just remember that the data you read in must be of the same type as the
variable into which it is being read and you will have little difficulty.
WHAT ABOUT READING FROM THE KEYBOARD?
Reading from the keyboard is similar to reading from a file, except that it uses the predefined internal
filename, Standard_Input, and if you omit a filename reference, the input will come from the
keyboard. It should be pointed out that the operating system will probably buffer the input data
automatically and give it to your program as a complete string when you hit the return key. It will
allow you to enter a complete string of as many integer values as you desire, and when you hit the
return, the entire string will be input and processed.
Modify the last file to read from the keyboard, and enter some numbers after compiling and running
it. Note that there is no End_Of_File when reading from the keyboard. Use a loop with a fixed
number of passes, or use an infinite loop and quit when a certain number is input such as -1.
HOW DO WE PRINT DATA ON THE PRINTER?
Example program ------> e_c14_p7.ada
The program named e_c14_p7.ada illustrates how to send data to your printer. The only difference in
sending data to the printer, from sending data to a disk file, is the use of the predefined name LPT1
for the external file name. This file is defined as the printer rather than a file, and as you can see from
the program, it operates no differently than any other file.
Other names may be applicable for the printer also, such as PRN, COM1, etc. Check your
documentation for the predefined names that mimic a file.
Be sure your printer is on line, then compile and run this program.
TEXT IO PRAGMAS
A pragma is a suggestion to the compiler and can be ignored by any implementation according to the
definition of Ada. Refer to Annex L of the Ada 95 Reference Manual (ARM) for the definition of the
predefined pragmas LIST and PAGE. Since these may not be available with any particular compiler,
their use should be discouraged in order to attain a high degree of program portability.
PAGE AND LINE CONTROL SUBPROGRAMS
Refer to the definition of Ada.Text_IO in Annex A.10.1 of the ARM for a large assortment of page
and line control subprograms for use with text I/O. These are required to be available with every
compiler, so you should study them to gain an insight into the page and line control subprograms
available to you in any Ada program. There should not be anything too difficult for you to understand
in these definitions.
PROGRAMMING EXERCISES
1. Modify e_c14_p4.ada to read from the keyboard and echo data to the monitor until a Q is
entered.(Solution)
2. Write a program named FLOATIN.ADA that reads floating point data from a file and displays
it. There is no standard on floating point input. Some compilers require a decimal point with at
least one digit before and after the decimal point. Some may not even require a decimal point
but will supply it to an input that looks like an INTEGER value. Experiment and determine
the characteristics of your compiler.(Solution)
Advance to Chapter 15
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 15
PACKAGES
PACKAGES ARE WHY ADA EXISTS
One of the biggest advantages of Ada, over most other programming languages, is its well defined
system of modularization and separate compilation. Even though Ada allows separate compilation, it
maintains the strong type checking among the various compilations by enforcing rules of compilation
order and compatibility checking. Ada uses separate compilation, but FORTRAN, as a classic
example, uses independent compilation, in which the various parts are compiled with no knowledge
of the other compilation units with which they will be combined. As we progress through this
material, and additional material to come later, do not be discouraged if you find many things to keep
in mind when doing separate compilations. The rules are not meant to be roadblocks, but are actually
benefits for you when you are working on a large complex system.
LET'S LOOK AT A PACKAGE
Example program ------> e_c15_p1.ada
Examine the file named e_c15_p1.ada for our first example of a separately compiled Ada package. A
package, as the term is used in Ada, refers to a collection of related entities, the collection being
composed of procedures, functions, variables, constants, types, subtypes, and even other packages. In
our present example, the package is composed of one type, and one procedure.
THE SPECIFICATION OF THE PACKAGE
Lines 4 through 8 define the specification of the package named AdderPkg, which is actually a very
simple package, and purposely kept simple for illustrative purposes. The type MY_ARRAY is
defined, as well as the procedure heading for Add_Em_Up in the specification part of the package.
The only things a user needs to know about the package in order to use it are defined in the package
specification, so it becomes the interface to the outside world. With a few defining statements, this is
all the user would need to know about the package, and he could be isolated from the actual details of
how the procedure does its job. We will see more about the topic of information hiding later in this
chapter.
Note that any entity that is declared in the specification part of the package can be used in any other
package that with's this package.
We have an unconstrained array type declared in line 5 which we have not yet studied in this tutorial.
The range for the subscript is defined by the "<>", which is a box that must be filled in later when we
define the actual type. We will cover this in detail in the chapter on advanced array topics.
THE BODY OF THE PACKAGE
Lines 12 through 23 define the body of the package, and it is distinguished from the specification by
the reserved word body in its header, and by the fact that the procedure is completely defined here.
Anything defined or declared in the specification part of the package is available for use here, just as
if it were defined at the beginning of this section. The procedure header is redefined here in full, and
it must exactly match the definition in the specification or you will get a compile error and no
compilation. There is nothing different about this procedure from any other procedure we have seen,
it just happens to be in the package body.
Note that any types, variables, constants, procedures, or functions, can be declared in the package
body for use within the body, but none of them are available outside of the package because they are
not defined in the specification part of the package. The variable declared in line 15 named Total is
not available outside of this package and there is no way it can be referred to outside of the package
without being declared in the package specification. In fact, since it is embedded within the
procedure, it would be hidden anyway, but the fact remains that no entities are available outside of
the package except those that are declared in the specification of the package.
It is not legal to place any subprogram bodies in the package specification.
Note that since the array has no defined limits, we must use attributes to define the loop range. Even
though you may find this a bit confusing, you will appreciate the flexibility found here after we study
some of the more advanced topics.
IT CAN BE COMPILED BUT NOT EXECUTED
This file can be compiled, but it cannot be linked and executed because it is not a complete program,
it is only a package containing a type and a procedure which can be called from another program just
like we have been calling the procedure Put in the Ada.Text_IO package throughout this tutorial.
One other point must be made before we look at a program to use this package, the specification and
the body do not need to be in the same file, they can be contained in separate files and compiled
separately. If this is the case, the specification must be compiled prior to compiling the body because
the body uses information generated during compilation of the specification. Actually, even though
they are in one file in this example, they are considered separately by the compiler, being compiled in
serial fashion. This file is said to be composed of two compilation units.
FILENAME VERSUS PACKAGE NAME
In all of the example programs so far in this tutorial, we have used the same name for the filename
and the program name, or the procedure name. This is not really necessary, but it was felt that an
additional level of complexity should be delayed until later. We have arrived at the time when an
explanation is needed.
When Ada compiles a program, it adds the result of the compilation into its library using the program
name, not the filename. This library entry includes all information needed to link the program with
the other needed library entries. Later when you use the linker supplied with your compiler, the linker
will collect all of the necessary compiled packages and subprograms and combine them into an
executable program.
In order to compile a program, it is necessary to give the filename so the operating system can find
the program for the Ada compiler, but in order to link a program, the filename is no longer of any use
because the name that really matters is the program name, and the program name is what the linker
uses. Ada allows identifiers to be of any arbitrary length, but your operating system has some length
limit, eight if you are using MS_DOS, therefore the Ada compiler may need to somehow make up a
new name if intermediate results are put into individual files. It will be up to you to determine how
your compiler stores intermediate results and how it makes up filenames for these results.
For simplicity, therefore, all program names have been limited to eight characters, and the same name
is used for the filename throughout most of this tutorial.
NOW TO USE THE NEW PACKAGE
Example program ------> e_c15_p2.ada
The example file named e_c15_p2.ada, illustrates how to use the previously studied package. There is
nothing different about this program from any other program we have used except that it uses the
package we defined and named AdderPkg, which it acknowledges in lines 5 and 6 where it tells the
system to with the package and to use it in the program at hand. It also uses the renaming statement
in line 16 which we will discuss later. The remainder of the program is simple, and you should have
no trouble deciphering it. The primary purpose of these two examples is to illustrate how to write a
library package.
In line 14 we declare an array of type MY_ARRAY and at this time we supply the range limits for
the subscript. You may begin to see the flexibility in this method of array declaration, but we will
study it in detail later.
THE with CLAUSE
It is finally time for a complete definition of just what the with clause does for us. When we with a
package into our program, we are telling the system that we wish to have everything that is declared
in the specification of that package available for our use in this program. Accordingly, the system
will look at the items declared in the specification for that package and every time we use one of
those items, it will see that we have the correct number of parameters and that we have the types
declared for each parameter correctly. Note that this happens during compilation and explains why
Ada is said to be compiled separately, but not independently of other compilation units. In a sense,
the resources available in the withed package act as though they are extensions to the Ada
programming language. Because of this, the with clause is called a context clause.
When you arrive at the linking operation, the withed packages are automatically added into the
executable file, as are any other packages that are withed into your program. Note that the package
named Standard is automatically withed into every Ada program, subprogram, or package. The
package named Standard defines many of the predefined entities such as INTEGER, BOOLEAN,
FLOAT, etc.
In a large program, it is possible to with the same package several times since it is used in several
packages. You can be assured that only one copy of the package will be included during the linking
operation. Multiple copies will not be stored.
One other point must be made before leaving this topic, and that is the fact that all with clauses must
be at the beginning of the program or package. There is a good reason for this. The dependent
packages, and hence the overall program structure must be given at the beginning of each package
making it easy to ascertain the overall structure without being forced to search through the entire
listing for each package.
THE use CLAUSE
The use clause in Ada allows you to use a shorthand when naming procedures, functions, variables,
etc, from a package. Instead of using the extended naming convention, or the so called "dot" notation,
and including the package name followed by the procedure name, dotted together, you can simply use
the procedure name and let the system figure out what package it is coming from. In most of the
programs we have studied so far, we have included the Ada.Text_IO package in a use clause. If the
use clause were omitted we would have to identify the package each time one of the procedures is
used. Put("This is Ada"); would have to be changed to read Ada.Text_IO.Put("This is Ada");, which
clutters up the listing a bit but removes all ambiguity.
Because it is possible to get a different procedure than the one you are expecting under very unusual
conditions of overloaded procedure names, the use of the use clause is falling into some disrepute in
the software engineering literature. Without the use clause, you are forced to type in additional
information for each procedure call. The presence of the package name prepended to each
subprogram call however, leads to no ambiguity and therefore follows the basic premise of Ada that a
program is written once but read many times. The extra keystrokes are worth the trouble to include
them.
Use of the use clause is a matter of personal taste or possibly a style dictated by a project style guide.
RENAMING A PROCEDURE
A procedure can be renamed in order to reduce the length of the identifier, especially if a rather long
extended name is required. It may be better to use a more descriptive name based on the actual use of
a general purpose procedure. The method of renaming is illustrated in lines 16 and 17 of this
program. Of most importance is the fact that the entire list of formal parameters must be repeated.
This is done so that the definition of the new procedure name is complete and should be of help
during program debugging.
Use of the new name, which is of course only a synonym and not a new procedure, is illustrated in
line 32 of this program.
If you compiled the previous file, named e_c15_p1.ada, you can compile, link, and execute this one
to see that the system knows how to link the two together.
COMBINING FILES
If you wish, you could combine the two files, provided you appended the second file to the end of the
first. The compiler would then compile all three in succession, after which you could link the results,
and execute the result. The library files must be compiled first so that the compiler can check the
types in the procedure call to see that they agree, so putting them before the calling program
conforms to this rule. If you did put them in a single file, you would still need the statements in lines
5 and 6 of e_c15_p2.ada to tell the system to with and use the library file defined earlier in the file,
because the compiler would consider them to be three separate compilations.
ANOTHER METHOD OF COMBINING FILES
Example program ------> e_c15_p3.ada
The example program named e_c15_p3.ada illustrates another way to combine the last two files, in
this case including the package in the declaration part of the program. The specification part of the
package is in lines 12 through 16, and the body is in lines 24 through 35. In this case, a new type is
defined between the two parts to illustrate that it can be done. Since the package is compiled as a part
of the main program, it does not have to be mentioned in a with statement. The compiler knows that
it is a part of the program, but the use must be mentioned to tell the system where to get the
procedure name and the type. Of course the use can be omitted and the extended naming convention
used for all references to the package.
Even though the body is defined after the variable New_Array is declared in line 20, this variable is
not directly available for use in the body, because the package structure effectively builds a strong
wall around the enclosed statements, and nothing can get in or out. The only inputs to and outputs
from the body are those defined in the specification part of the package. Of course the variable
New_Array is available to the procedure because it is passed in as a parameter, but is not directly
visible.
One other difference occurs here that is different from the last two files. This embedded package is
not available for use by any other program, because it is enclosed within this program, and is not
therefore a library package. The entire file contains only one compilation unit.
ANOTHER KIND OF SEPARATE COMPILATION
Example program ------> e_c15_p4.ada
The example file named e_c15_p4.ada illustrates still another method of separate compilation. This
program is identical to the last except that the package body is removed to a separate file for separate
compilation, and is called a stub. The statement in line 24 indicates to the compiler that the body will
be found elsewhere. Although this illustrates separate compilation of a package body, the same
method can be used for separate compilation of a procedure. This could be used if you wished to
remove a large procedure from the logic defined here to make it more manageable.
Example program ------> e_c15_p5.ada
The separately compiled body is found in the file named e_c15_p5.ada, which begins with the
reserved word separate which indicates that this is a stub. The main program, or whatever other
package, procedure, or function, that uses this stub is defined in parentheses following the reserved
word separate to tell the compiler where this is used. This stub cannot be called or used by any other
program, because it is in truth part of the program Adder3, not a general purpose program. Any
variables, types, procedures, etc, that are available for use at the point where the stub is used, are
available at the point where the stub is defined. The stub therefore has the same environment as the
environment existing at the point of use, in this case line 24 of e_c15_p4.ada.
ORDER OF COMPILATION FOR THE STUB
Since all of the variables, types, etc must be made available to the stub, the using program must be
compiled before the stub itself is compiled. After both are compiled, the program can be linked and
executed. You should compile e_c15_p4.ada first, then e_c15_p5.ada should be compiled, and finally
ADDER3 should be linked. You will then have an executable ADDER3 program.
ORDER OF COMPILATION IS NOT MYSTERIOUS
The example files included with this chapter are intended to illustrate to you the required order of
compilation in a meaningful way, not as a group of rules to be memorized. If you understand the
dependencies of files on one another, the order of compilation will make sense, and you will be able
to intelligently arrange your programs to use the Ada type checking between the various separately
compiled files. Remember that Ada, unlike Pascal, was designed to allow the development of huge
programs that require separate compilation facilities, and yet retain the strong type checking between
modules.
A PACKAGE WITH AN INITIALIZATION PART
Example program ------> e_c15_p6.ada
The example file named e_c15_p6.ada illustrates one more feature of a package. This program is
nearly identical to the first program in this chapter. Both files which comprise the first example
program have been incorporated into a single file for ease of compilation, and the variable named
Total has been made global within the package. Since it is global within the package, it can be
referred to by any subprogram in the package or in the initialization section as illustrated in lines 23
and 24. This section is optional but can be included in any package.
Note that the initialization section is only executed once, at load time. For that reason, this program
will not operate exactly as the first one did. The result that is output is identical in both cases, but the
additional calls will not produce the same result because the variable Total is not cleared to zero
during each call in this example program as it is in the first example. The initialization section only
initializes the variable once, and it is impossible to call this section of code from within the
executable part of the package.
A FEW OF THE STANDARD PACKAGES
Ada 95 provides many new packages to improve the programming model and prevents errors on the
part of the programmer, and to provide additional conveniences when programming. We have already
discussed a few of the additional packages during our study of Ada earlier in this tutorial. We will
make a few comments on a few more that you may find useful, but really didn't fit in well anywhere
yet in the tutorial.
package Standard - This is the root of all packages in Ada 95 and is where such things as
INTEGER, FLOAT, BOOLEAN, and other such entities are defined. It is inherently withed into
every Ada compilation unit automatically.
package Ada.Command_Line - This is actually a part of the above mentioned Standard package
but that fact is really invisible to you. This package provides the ability to retrieve any parameters
provided by the user when he begins execution of the resulting program. This package provides a
means to retrieve those parameters in a portable manner. This did not exist in Ada 83, so each
compiler writer came up with a non-portable way to do this.
package Ada.Numerics.Elementary_Functions - This package provides the trig functions along
with a floating point exponentiation operator, and many other math functions.
package Ada.Characters.Latin_1- This package replaces the ASCII package which was available
with Ada 83 but is now obsolete. This new package provides definitions of the ASCII character set.
INTERFACES TO OTHER LANGUAGES
Annex B of the ARM defines the method of interfacing to other languages which your compiler may
or may not support. The packages named Interface.C, Interface.COBOL, and Interface.Fortran, if
available with your compiler, will provide you with the ability to combine code in those languages
with your Ada code in a well defined manner. Of special importance is the method of handling
strings, pointers, arrays, and structures since they can be so different for other languages. The
interfaces consist of various pragmas and subprograms to define the interface characteristics. You can
study the details on your own, if you ever have the need to interface to a language supported by your
Ada compiler.
PROGRAMMING EXERCISES
1. Remove the package body from e_c15_p3.ada and make it a stub. Compile the two resulting
files in the correct order and link and execute the resulting program.(Solution 1)(Solution 2)
2. Remove all use clauses from the program named e_c14_p1.ada in the last chapter, and prefix
the I/O procedure calls with the proper package names. It should be clear to you that there can
be no naming conflicts after this is done.(Solution)
3. Change the name of the two compilation units in e_c15_p1.ada without modifying the
filename, and modify e_c15_p2.ada to correspond to the new package name. Use a name that
contains more characters than your operating system permits for a filename. Compile each file
and link them together to result in an executable program.(Solution 1)(Solution 2)
Advance to Chapter 16
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 16
EXAMPLE PROGRAMS
WHY IS THIS CHAPTER INCLUDED?
It would be a real disservice to you to discuss all of the constructs available with Ada, then simply
drop you in the middle of a large program and tell you to put it all together. This chapter is intended
to illustrate to you, with simple examples, how to put some of these topics together to build a
meaningful program. The examples are larger than any programs we have examined to this point, but
they are still very simple, and should be easy for you to follow along and understand what they are
doing.
THE CHARACTER STACK
Example program ------> e_c16_p1.ada
The program named e_c16_p1.ada illustrates how you can define your own stack for use in your
programs. A stack is a list of homogeneous items that grows and shrinks in such a way that the last
item entered will always be the first removed, thus it is often referred to as a Last In, First Out (LIFO)
list. In order to keep it simple, we will only allow the stack to store CHARACTER type variables,
although it could be defined to store any type of variables we desired, even arrays or records.
THE SPECIFICATION PACKAGE
The specification package is defined in lines 2 through 20, and gives the user everything he needs to
know to use the character stack package. You will see that the user does not know how the stack is
implemented, or even how big the stack can grow to unless he looks into the package body, and if we
hide the body from him, he has no way of knowing this information. All he can do is use the package
the way we tell him it works. In a real system, we would have to tell him how big the stack could
grow, because that would probably be valuable information for him when he designed his software,
but for our purposes we are not going to let him know the maximum size. Of course, we must then
provide a means to be sure that he cannot force the stack to get larger than its maximum size. The
procedure Push is defined in such a way that if the stack is full and the character will not fit, it is
simply not put on the list. The user must check if the stack is full by calling the function Is_Full
which returns a BOOLEAN value of TRUE if there is no additional room on the stack.
Careful study of the package specification will reveal that enough functions and procedures are
included to allow effective use of the character stack. The package body simply defines the actual
implementation required to do the work defined in the specification. Once again, you should be able
to understand the package body since there is nothing here which is new or strange to you. It should
be pointed out to you that this is actually not a very efficient way to define a stack, because it is
limited to a single type, but it serves our purposes well as an illustration. When we study generic
packages in part 2 of this tutorial, we will see a much better way to define the stack so that it can be
used with additional types. Chapter 33 of part 2 of this tutorial includes this same program rewritten
as a generic package which can be used with virtually any data type. You should compile this file in
preparation for using it in conjunction with the next example program.
HOW DO WE USE THE CHARACTER STACK?
Example program ------> e_c16_p2.ada
The example program e_c16_p2.ada uses the character stack included in the last program. Notice the
with and the use clause in lines 4 and 5. These tell the system to get a copy of CharStak, and make it
available for use in this program. Two string constants are defined for use later, and two procedures
are defined to fill the stack, or add a string of characters to the stack one at a time, and to empty it a
character at a time. The main program displays each of the string constants on the monitor, then uses
the two procedures to reverse the string of characters and output the reversed strings on the monitor.
You should study the program until you understand how it works, even though the program itself is
not nearly as important to understand as the concept of the separately compiled package. Be sure to
compile and run e_c16_p2.ada to see that it really does what you expect it to do.
THE DYNAMIC STRING PACKAGE
Example program ------> e_c16_p3.ada
The next example program, named e_c16_p3.ada, fulfills a promise made when we were studying
strings. At that time, we mentioned that Ada 83 did not have a dynamic string capability but Ada 95
does have several packages that are defined in the ARM as part of the Ada environment. It will be
advantageous for you to learn how to use these supplied packages for use in production programs.
For the time being, it will be beneficial to spend some time studying the dynamic string package
included in e_c16_p3.ada because it illustrates how to use many of the constructs we have studied in
this tutorial to this point
The dynamic string package, although implementing a complete version of a dynamic string package,
is not submitted as the final word in string packages. In fact, it has a problem which we will describe
later in this chapter. Even after we fix the problem, there is a better string package we can use in all
of our production programs. It will also be discussed later in this chapter.
One construct is used which we have not yet studied in this tutorial although we did mention it in
passing in the last chapter. Line 33 contains an unconstrained array type declaration. Its use is
illustrated in lines 9 and 10 of the program named e_c16_p4.ada. The unconstrained array will be
studied in part 2 of this tutorial. Other than this one construct, e_c16_p3.ada uses no portion of Ada
that we have not yet studied in this tutorial, so it does not take advantage of the advanced constructs
of Ada. It is meant to be a teaching aid only, but you could use it in a production program, which you
are welcome to do as a user of Coronado Enterprises tutorials. You are granted the right to use any of
the included software for whatever purpose you deem necessary, except commercialization of the
tutorial itself. You may need to modify a program slightly for your own purposes, and you have
permission to do so.
The dynamic string, as defined here, has a maximum length of 255 characters, with the dynamic
length stored in the first element of the string which has a subscript of zero. The string must therefore
be declared as a CHARACTER string with a lower bound of 0, and an upper bound representing the
maximum length that the string will ever be asked to store. This definition comes from Borland
International's implementation of Pascal, which they market as TURBO Pascal.
THE DYNSTRNG SPECIFICATION
The specification package of DynStrng is defined in lines 29 through 116 of the file which is well
commented, giving a thorough definition of the package, and describing how to use it. The body is
given in lines 124 through 384, and give the actual implementation. Note that some software
developers give you only the specification part of the package, which is all you really need to use it,
and hide the body from you. If they provide the compiled body, and the specification source, you
have all you need to use the package with that specific compiler. Your compiler came with the
package Ada.Text_IO, a standard package, and your compiler writer almost certainly did not provide
you with the source code to the body. The Ada.Text_IO package, as defined in the ARM, is only the
specification part of the package Ada.Text_IO. It can be found in Annex A.10.1 of the ARM.
You should take notice of the fact that we are overloading the name Put on line 36 of this package so
we can use it to output a dynamic string to the monitor. The system knows which one we wish to use
by the type of the actual parameter used. The careful observer will notice that we use the Ada.
Text_IO version of Put in line 132 of the procedure used to overload the name Put. Continuing the
discussion of overloading, you will see that we define two procedures with the same name in lines 60
and 63, once again depending on the types to select the proper procedure for each call.
Study the DynStrng package until you feel you understand it fairly well, then compile the file to
prepare for the next program.
USING DYNAMIC STRINGS
Example program ------> e_c16_p4.ada
The example program e_c16_p4.ada is designed to use the dynamic string package in various ways
by defining strings, inserting characters or strings, deleting portions of strings, and displaying the
results. This program was written to test the DynStrng package so it does a lot of silly things. There
is nothing new or innovative in this utilitarian program, so you will be left on your own to
understand, compile, and execute it.
A PROBLEM WITH e_c16_p3.ada
The DynStrng package works just fine the way it is used in the TryStrng package, but a problem
appears when it is used to copy a string constant into a dynamic string. The problem is due to
overloading the name Copy in lines 60 and 63 of the DynStrng specification, and is best illustrated
with an example. Consider the following line of Ada code;
Copy("Line of text.", Stuff, Result);
In this case, the compiler finds that the string constant could be of type STRING or of type
DYNAMIC_STRING, and does not know which overloading to use, so it gives a compile error
saying that it cannot resolve the type. The way to use this package to do this would be to tell the
compiler which one to use by qualifying the string constant as shown in this line of code.
Copy(STRING'("Line of text."), Stuff, Result);
This will completely resolve the ambiguity and the program will then compile and execute properly.
You should include these two lines in a test program to see for yourself that this does resolve the
ambiguity.
NOW TO FIX THE PROBLEM
There is an excellent solution to this problem that will render this dynamic string package flexible
and useful but it requires the use of a discriminated record which we have not yet studied in this
tutorial. In part 2 of this tutorial, we will revisit this dynamic string package and offer a much more
flexible package for your information and use.
The DYNAMIC_STRING package is a great package for you to study as an illustration of how a
package is developed, and how a typical package works. However, it has been superseded by two
new packages available for use with any Ada 95 program. The packages named Ada.Strings.
Bounded, and Ada.Strings.Unbounded are parts of the Ada 95 standard library and are available for
your use. You should learn their capabilities and limitations thoroughly because they can save you a
lot of time.
HOW OLD ARE YOU IN DAYS?
Example program ------> e_c16_p5.ada
The example program named e_c16_p5.ada, is a silly little program, but intended to illustrate how to
effectively use the keyboard for input to a program. This program will ask you for today's date, and
your birthday, then calculate your age in days. There is no provision for leap year, or even for months
with other than 31 days. It is intended to illustrate how to put together an interactive program that
could be useful in some way.
Once again, since we have not studied the advanced topics of Ada yet, we have a limited number of
constructs to use. The example programs at the end of Part 2 of this tutorial repeats this program, but
uses the predefined Ada package Calendar to get today's date rather than asking the user to supply it.
The advanced topics will add flexibility to your use of Ada. Compile and run this program to get a
feel for how to write an interactive program.
PROGRAMMING EXERCISES
1. Add an additional function to e_c16_p1.ada to return a value indicating how many more
characters can be pushed onto the stack.(Solution)
2. Use the new function in e_c16_p2.ada to output a message to the monitor indicating the
amount of space remaining on the stack at the end of the Fill_The_Stack procedure.(Solution)
3. A major programming assignment - The best way to learn Ada is to use it, so the following
programming suggestion is given. After studying the package e_c16_p3.ada, put it away and
attempt to duplicate it from scratch. You will find that you will use nearly every topic covered
in part 1 of this tutorial, and if you get completely stumped, you will have the supplied version
of the package to help you over the rough spots. Your goal should be to duplicate the supplied
package so closely that the existing program named e_c16_p4.ada can use your new version.
If you find e_c16_p3.ada too big for a first step, you may wish to try to duplicate e_c16_p1.
ada in the same manner before jumping into the dynamic string effort.
Advance to chapter 17
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 17
EXCEPTIONS
EXCEPTIONS ARE NOT NEW TO YOU
Assuming you have completed part 1 of this tutorial, you have seen many references to exceptions,
but we have said little about how you can use them. The purpose of this chapter is to instruct you on
the use of exceptions, and by the time you complete it, you will have the ability to use exceptions to
develop a program with its own error handling ability.
WHY DO WE NEED EXCEPTIONS?
The original charter for the development of Ada included the ability to operate in a real-time
environment. You already understand, if you have much programming experience, that if it is
possible for an error to surface, it will eventually surface. Many programming languages simply
terminate operation if a "fatal error" is detected, but this could be a disaster if the program was
controlling a real-time system upon which human lives or safety depended. A 747 in final approach
to the airport, or a system used in a hospital operating room would be two examples of systems that
simply could not be terminated abruptly because a bad data point was somehow accumulated. The
careful application of Ada exceptions will allow the software to gracefully recover from such a
situation rather than aborting operation completely.
OUR FIRST EXCEPTION
Example program ------> e_c17_p1.ada
Examine the program named e_c17_p1.ada for our first example program with an exception handler.
Ignore lines 18 and 19 for the moment and you will have a program that is not at all unusual, and
should pose no problem for you to understand. The program does have a carefully introduced error
however, because when we reach a value of 4 for Index in line 14, we will be attempting to divide by
zero. Dividing by zero is not allowed in any programming language, because the answer is infinite
and therefore undefined. The Ada runtime system will, by definition, cause the exception named
Constraint_Error to be raised, which is the Ada way of saying that a divide by zero was attempted.
This signals the system to do something about it. (Actually, there are many other ways to get the
Constraint_Error exception raised but we will worry about them later.)
The Ada system will search, in a very definite way, for any instructions we have given about this
error, and if it finds none, will terminate operation of the program after issuing a message concerning
the error. If we have given instructions about what to do with the error, it will execute the instructions
and continue operation as we direct it to do. The method of giving the system these instructions is
illustrated in lines 18 and 19.
HOW ARE EXCEPTIONS HANDLED?
When any exception is raised, the system immediately looks at the end of the current block or
subprogram for the reserved word exception. If it is found, and if the specific exception that was
raised is defined there, the instructions associated with that exception are executed, and the
subprogram or block is exited.
To define a handler for a specific exception, the reserved word when is used, followed by the name
of the exception, and finally the sequence of statements to be executed following the => operator.
The sequence of statements can be of arbitrary complexity, but should be kept simple due to the
nature of exception handling. In this case, we output a message to the monitor and do nothing else.
As many different exceptions as desired can be handled at the end of any block or subprogram by
adding additional constructs of the form,
when <exception-name> => instructions;
following the single instance of the reserved word exception. We will study examples of multiple
exception handlers later in this chapter.
WHAT HAPPENS FOLLOWING EXCEPTION HANDLING?
Following handling of the exception, the program executes an otherwise normal return to the calling
program, and the normal sequence of instructions in the calling program is executed. Note that it is
impossible to jump back into the subprogram or block in which the exception was raised from the
exception handling routine at the end of that block. In this case, because of the logic used, the loop
defined in line 10 is terminated early because we essentially jumped out of the loop and the program
is ended. If an exception handler or a group of exception handlers is included, it must be the last thing
in the block. If normal execution of the block reaches the end of the executable statements by coming
to the reserved word exception, the block is terminated normally. You cannot drop into the exception
handler at the end of a block. The only way to get to the exception handling code is through raising
an exception.
In spite of the additional questions you have at this point, compile and execute this program. Observe
the results, and if you do not understand the output, reread the above text until you do, because these
fundamental points are essential to understanding the entire topic of exceptions.
LET'S USE SEVERAL EXCEPTIONS
Example program ------> e_c17_p2.ada
Examine the program named e_c17_p2.ada for additional examples of exceptions. This program will
answer many of your questions about exceptions.
In the last program we terminated the loop when the exception was raised, but we may desire to
continue execution of the program following the exception. The first procedure in this program is
logically identical to the last example program except the loop is moved to the calling program.
When the divide by zero is detected by the system, which raises the Constraint_Error exception, the
exception is handled by the exception handler defined in lines 17 and 18, and the return to the calling
program is effected. In this case however, when control returns to the calling program, we are still
inside of the loop, and the loop completes normally. This should indicate to you that by careful
selection of where you handle exceptions, you can control the overall result. We will see more about
this as we continue our study of exceptions.
The logic of the second group of instructions, found in lines 49 through 64, is identical to the logic of
the first group as studied in the last paragraph. The only difference is that the procedure has been
changed into a block and inserted into the code in an inline fashion. This has been done to illustrate
the use of an exception in a block of code, and to illustrate that the exception handler for the block of
code is put at the end of that block. After the exception is raised and handled, execution begins at the
first statement following the block. Because the block is contained within the loop, the exception is
handled within the loop and the loop runs to completion.
MULTIPLE EXCEPTION HANDLERS
Finally, we come to the section of code in lines 66 through 69, consisting of a simple loop calling the
procedure New_Divide_Loop. The procedure itself, defined in lines 21 through 40, contains an
example of a new operation, the ability to make up our own exception, raise it our self, and handle it
with our own exception handler.
Line 22 declares the identifier My_Own_Exception as being the name of an exception, and is
defined in much the same way that we would declare a variable. We cannot assign a value to it, but
we can raise it anywhere within its defined scope which is the same as the scope of a variable
declared at the same place. The exception is automatically initialized to the "not raised" condition by
the system.
Beginning in line 34, we define three different exception handlers, which will cover any exceptions
raised anywhere within this procedure. The first two are named exception handlers but the third
handler uses the reserved word others to indicate that it will be used for any exceptions that are not
handled by the two named exception handlers. The others clause is optional, but if it is included, it
must be last.
RAISING AN EXCEPTION
If we reach line 28 with a value of 4, which we eventually will because of the logic of the calling
program, we will detect the divide by zero that would be attempted upon reaching line 31. Instead of
letting the system generate the exception named Constraint_Error, we generate our own exception
named My_Own_Exception, using the reserved word raise followed by the name of the exception.
As soon as we raise this exception, the system jumps to the end of the block, looks for the reserved
word exception, which it finds, then looks for the exception handler with the name that was raised.
Upon finding it, the statements are executed, resulting in a message being output to the display, and
we return to the calling program.
In this case, the system will not raise the exception Constraint_Error, because we are detecting the
error before it actually happens. You could raise it yourself by inserting the statement "raise
Constraint_Error;" somewhere in this procedure, possibly when the value of Index is equal to 3. It
would be a good exercise for you to insert that in the code to see that you can raise one of the system
exceptions as well as your own.
Be sure to compile and execute this program to verify proper operation according to this description
and to ascertain your understanding of the same.
Note that if an exception occurs, formal parameters of mode out or in out are not updated since a
normal return is not accomplished. Partial results will therefore not be returned to the calling
program. This is not illustrated here, but is left for the student to investigate if desired.
WHAT ARE THE PREDEFINED EXCEPTIONS?
There are four predefined exceptions which can be raised by the system to indicate a very specific
problem. A brief definition follows;
1. Constraint_Error - This will occur if something goes out of its assigned range.
2. Program_Error - This will occur if we attempt to violate an Ada control structure such as
dropping through the bottom of a function without a return.
3. Storage_Error - This will occur if we run out of storage space through either recursive calls
or storage allocation calls.
4. Tasking_Error - This will occur when attempting to use some form of tasking in violation of
the rules.
WHAT ABOUT AN UNHANDLED EXCEPTION?
Example program ------> e_c17_p3.ada
Examination of the program named e_c17_p3.ada will reveal what happens if an exception is raised
that is not handled by the program. In a word, the program will be terminated, but we need to
understand how termination occurs so we can intelligently prevent it.
There is a loop in the main program which calls two procedures successively, Divide_By_Zero and
Raise_An_Error. The first procedure is identical to that in the first two example programs and the
only exception raised is Constraint_Error, which is handled properly.
The second procedure has its own exception defined, named My_Own_Error which it raises and
handles itself in the manner defined previously in this chapter. It also has a divide by zero problem in
line 26 that will raise the exception Constraint_Error when Count is equal to 6. Of course, the logic
is defined to make this happen and illustrate the error.
PROPAGATION OF EXCEPTIONS
When the exception Constraint_Error is raised at line 26, the system searches for the reserved word
exception which it finds in line 31 at the end of the procedure. It then searches for a sequence of
statements for Constraint_Error which it does not find. Since an exception handler is not found
within the procedure, the exception is propagated to the calling program in such a way that the
exception appears to have been raised by the calling statement. In this case it will appear to the logic
as if the exception Constraint_Error was raised by the statement in line 39. Once again, the
exception rules are applied, and the system searches for an exception section at the end of the block
or subprogram, in this case being the main program. Finding the reserved word exception in line 43,
the system looks for the desired exception handler, which it finds and executes, then drops out of the
bottom of the main program and returns to the operating system.
If there were no handler for the exception, the exception would be propagated to the operating
system, and it would issue some sort of nasty message on the standard output device about an
unhandled exception leading to program termination.
It should be somewhat obvious to you that if you added another level of subprogram nesting, you
could report the error yourself, and possibly recover operation of the program. It is all a matter of
program definition.
CAN YOU EXECUTE AN EXCEPTION WITHOUT RAISING IT?
As mentioned before, the section of code at the end of a block or subprogram, following the reserved
word exception, is never executed without raising an exception. It can never be executed by dropping
into it.
Be sure to compile and execute this program to observe the operation of the exceptions.
EXCEPTIONS CAN OCCUR DURING DECLARATIONS
Example program ------> e_c17_p4.ada
Examine the program named e_c17_p4.ada for an example of an exception that occurs during the
declaration part of the program.
When a procedure is called, its declarations are elaborated prior to the logic being executed, as we
have stated before. If one of the declarations cannot be properly elaborated, then an error occurs and
an exception is raised. Examining the procedure Try_It will reveal an error in lines 8 through 10,
where he variable Funny is declared to be of type LIMIT_RANGE with limits of 14 through 33,
then it is initialized to the value 8. Since this is out of the allowed range, the exception
Constraint_Error will be raised. The executable part of the procedure is not yet ready for use, so the
exception handler defined within it cannot be used, and the exception will be propagated to the
calling program where it will be handled just as if it occurred in the calling statement, which is line
22 in this case. The exception will therefore be handled by lines 24 through 26 of the procedure
Try_To_Fix_It. Note that we never executed the code in the procedure named Try_It.
Be sure to compile and run this program, then study the results.
ADDITIONAL PREDEFINED EXCEPTIONS
You will find that there are actually additional exceptions predefined by your compiler, but these are
all defined in additional packages supplied with your compiler. Packages such as Ada.Text_IO, Ada.
Sequential_IO, or Ada.Calendar (to be discussed later with tasking), have some number of
exceptions defined as a part of their interfaces, but there are only four exceptions predefined as a part
of Ada. These were listed and discussed earlier.
A FEW MORE TOPICS CONCERNING EXCEPTIONS
Example program ------> e_c17_p5.ada
The example program named e_c17_p5.ada illustrates a few additional topics about exceptions and
illustrates how they are used in a package. This is a very strange program with lots of exception
handling examples for your study. You will be left on your own to study the overall operation of this
program, but the unique exception handling techniques will be pointed out to you.
The package body contains a section of initialization code in lines 37 through 49 which is composed
of nothing but a null statement and several exception handlers. These are only used during
initialization of the package since they are not within the executable portion of either of the
subprograms. You will notice that the exception named Funny_Add_Error is declared in the
package specification so it is visible in the exception handler in line 46, but the exception named
Funny_Subtract_Error is not visible there because it is declared within the function. We will see
soon however, that even this exception can be propagated to the main program.
When the program is executing, a call to the function Subtract_One raises the exception
Funny_Subtract_Error which is handled by the exception handler at the end of the function in line
32. A message is displayed and the same exception is raised by the isolated raise statement in line 34.
This statement simply raises the exception that caused the jump to the exception handler in the first
place. The isolated raise statement can only be used within an exception handler. The exception is
passed on to the calling program, even though it has already been handled here.
Because the exception named Funny_Subtract_Error is not visible to the main program, it cannot
handle it by name but even this exception can be handled by an others clause as is done in line 68.
After printing a message, the same exception is once again raised in line 70 where it is passed on to
the operating system. You will see when you execute this program that the exception is known by
name to the operating system. It will give you a nasty message about the unhandled exception and
terminate operation.
THE others CLAUSE IN AN EXCEPTION HANDLER
If the others clause is used, it must be the last in the exception handler list and it cannot be combined
with any other exceptions such as illustrated in line 64. As in other Ada constructs, two or more
exceptions can be "or"ed and use the same exception handler. Numeric_Error was available inn Ada
83, but is considered obsolete in Ada 95. The name Numeric_Error is a synonym for
Constraint_Error in Ada 95 to permit legacy code to compile without error.
Note line 59 where an exception is renamed to reduce the length of its name. Any exception can be
renamed in a similar fashion.
Be sure to compile and execute this program and spend the time necessary to understand the
exception propagation illustrated here.
WHAT IS AN EXCEPTION OCCURRENCE?
Many times it is adequate to simply report that an exception occurred and the general nature of the
exception is sufficient to permit a recovery from the exceptional condition. There are times however,
that it is necessary to provide additional information about the exception and what caused it. The
exception occurrence is available in a predefined Ada 95 package for this purpose. The exception
occurrence gives a unique name to one instance of raising an exception and provides hooks to
completely analyze where and when the exception occurred. As always in this tutorial, an example
program provides the best illustration, so examine the example program named e_c17_p6.ada.
Example program ------> e_c17_p6.ada
In line 2 we with the package named Ada.Exceptions which provides us with the ability to name and
use an exception occurrence, and we name one in line 8. The name Except_ID can be used to refer to
any single exception at a time, but can be reused to refer to any other exception at a later time. This
example program executes a loop with a contrived error in it, a divide by zero. In fact, you may
recognize it as a modification of the first example program in this chapter.
When we arrive at the divide by zero condition, the exception Constraint_Error is raised and the
exception handler defined in line 23 is found and execution begins. However, because of the
occurrence name given immediately following the reserved word when, this particular occurrence of
Constraint_Error is given the name Except_ID. Once we have the occurrence name, we can use it
to retrieve a formatted message with the name of the exception, even if the exception name in no
longer within scope. This is done via a call to Exception_Name which is a part of the Ada.
Exceptions package. This is illustrated in line 28 where the returned line of text is simply copied to
the monitor. We can also get a more detailed message including a complete call stack listing by
calling the function named Exception_Message with the name of the exception occurrence as the
only parameter. This result is also copied to the monitor for inspection.
The actual format is implementation dependent, and your compiler may give a very verbose line of
text or a very sparse line of text. The compiler used for this compilation emitted a very scarce text.
If we were to correct the error and allow the program to continue, another divide by zero condition
would cause the Constraint_Error and the Except_ID exception occurrence could be used to
analyze that particular exception. It should be obvious that you could use this to log certain
exceptions to a log file for post execution analysis of what caused some particular catastrophic
failure. If properly planned, such a log file would have a complete history of system operation prior
to failure.
USING THE OCCURRENCE MORE THAN ONCE
In line 34 of the example program, we use the same occurrence name to retrieve and display
information about any occurrence of the Storage_Error exception. This indicates that the exception
occurrence acts just like a variable and can be used to refer to any exception that occurs during
execution of the program. Note that it is very unlikely that the Storage_Error exception will be
raised in this program, but is given here only for illustration.
The same name can be used with the others condition to cover any otherwise unhandled exceptions
that occur.
Be sure to compile and execute this program with your compiler to see the output format provided for
you. There is no standard way to format this data, so your output could look very different from the
example output provided in the result of execution. The same information will be provided for you in
some meaningful manner.
PROGRAMMING EXERCISES
1. Change line 31 of e_c17_p2.ada to cause a divide by zero when Index is equal to 2 and see
that both exceptions will be handled correctly.(Solution)
2. Also in e_c17_p2.ada, declare a new exception in line 22, and raise it in line 29 to see how the
others clause handles it.(Solution)
Advance to Chapter 18
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 18
ADVANCED SUBPROGRAM TOPICS
In part 1 of this tutorial we covered the topic of subprograms in some detail, but there are many other
things to discuss about them, so we return to them for more advanced topics.
DEFAULT PARAMETERS
Example program ------> e_c18_p1.ada
Examine the program named e_c18_p1.ada for some examples of default parameters used in the
definition of a procedure. The procedure has four formal parameters, of which the first is of mode in
out, and the other three are of the mode in. The three in parameters have default values of zero
assigned to each of them. When we call this procedure, we are not required to supply a value for each
variable, and those we do not supply a value for will be defaulted to zero upon execution. Of course
the first variable in the list, named Total, must have a variable name supplied so it can return a value.
Therefore, it cannot be defaulted.
The procedure itself, and the first two calls to it, in lines 29 and 30, should pose no problem for you
to understand. When we arrive at line 31, however, we have a few things to point out.
NAMED NOTATION FOR ACTUAL PARAMETERS
We are using the named aggregate notation for the actual parameters in line 31, so they can be listed
in any order, but due to the defaults defined in the procedure header, we do not have to specify every
parameter, allowing the default values to take effect upon a call to the procedure. You will see, when
you compile and run this program, that Cows and Pigs will have the default values of zero. Lines 32
and 33 also use the named aggregate notation and should be clear as to their operation. Line 34 uses
the mixed aggregate notation, and as we discussed before, the positional aggregate notation can be
used initially, but after switching to the named notation, all remaining entries must be named, unless
they are allowed to default. Line 35 illustrates the degenerate case where only the result is used, with
all three input variables defaulting to zero.
PARAMETERS WITH out MODE CANNOT BE DEFAULTED
Since the parameters that are of either mode in out or mode out must be able to return a value, they
must have a variable defined as their actual parameter, and cannot therefore be defaulted.
Default parameters are not new to you, because you have actually used them in procedure calls
before. When you call the procedure New_Line, you have an optional number following it as in
New_Line(2). The number of lines to space up on the monitor is defaulted to one in the package Ada.
Text_IO, which was supplied to you with your compiler, but you can override the default by
inserting a value for the number of lines. It is defined in Annex A.10.1 of the Ada 95 Reference
Manual (ARM), where the formal parameter named Spacing is defaulted to the value of 1. Refer to
either your documentation or the ARM and see this in use.
DYNAMIC DEFAULT PARAMETERS
Example program ------> e_c18_p2.ada
The program named e_c18_p2.ada is identical to the last example program except for one detail. The
definition of the default values of the formal parameters are declared differently here. You will notice
that the default values are not only arithmetic combinations, but the values to combine are the results
of function calls, where the values are dynamically evaluated each time the procedure Animals is
called. The default values are constants for each call of the procedure, but they are evaluated for each
call and could therefore be different each time the procedure is called and executed. As mentioned
previously, this is called elaboration. If the return from Cow_Constant, in line 12, returned the value
of a global variable for example, the main program could modify the value of the global variable and
therefore modify the value of the default variables prior to each call to the procedure. It would
therefore be possible to set up different default values in each of several different procedures based
on the currently read time of day, the ambient temperature, or whatever other variable conditions
could be read into the system.
Be sure to compile and run this program and observe the results, comparing them with the results you
expected the program to output.
THE MYSTERY OF RECURSION
Example program ------> e_c18_p3.ada
This topic will be no problem for the experienced Pascal programmer, but for the FORTRAN
programmer, it may be an entirely new and somewhat perplexing topic. Stay with it, and you will see
exactly what recursion is and how to use it effectively. Examine the example program named
e_c18_p3.ada, which is the simplest recursive program possible, but which is excellent for describing
what recursion is and how it works.
Beginning at the main program, we assign the variable named Index the value of 7 then call the
procedure named Print_And_Decrement, taking along the value of Index as an actual parameter.
Arriving at the procedure itself, we use the name Value for the formal parameter, and we display the
value on the monitor with an appropriate line of text. Continuing on to line 15, we decrement the
value of the passed variable then compare the result to zero. If the value is greater than zero, we call
the procedure named Print_And_Decrement taking along the newly decremented value called
New_Value. Here is where the FORTRAN programmer notices something new. We are calling the
procedure from within itself, and that is just what recursion is. Assume for a moment that we call
another complete copy of the procedure, decrement the value once again, and if it is still not zero, call
another copy of the procedure. Eventually, the value of the passed variable will be reduced to zero
and the procedure calls will all be completed, each returning to the procedure that called it until we
arrive once again at the main program.
You should compile and run this program to see that it really does what we say it does, then return for
additional discussion of this program and what it is doing. This is a really dumb way to count from 7
down to 1, but it is a very simple way to illustrate the use of recursion. Later in this tutorial, we will
have illustrations of excellent uses of recursion for your instruction.
WHAT ACTUALLY HAPPENED?
When we called the procedure Print_And_Decrement, it began by elaborating its formal variables
and assigning them the values passed by the calling program. These are stored on the stack, an
internal portion of memory set aside by the Ada system to store dynamic variables and constants, and
are available for later use. The local variables are then generated and elaborated, although in this case
there was only one, and stored on the stack also with means to refer to them. Finally, the program
code itself is actually executed, and when it is completed, the local variables and formal variables are
erased from the stack and no longer exist. In a recursive call, the stack grows with each new call, and
when control returns to an earlier call of the code, the variables for that call are still on the stack and
available for use. In this program, we actually have only one copy of the executable code stored, but
we have many copies of the formal variable and the local variable stored on the stack.
ALL ADA SUBPROGRAMS ARE RE-ENTRANT
If you have experience in systems programming and understand what it means for a program to be re-
entrant, you will understand how this means of variable storage allows all Ada subprograms to be re-
entrant. The ARM requires that all Ada subprograms be re-entrant, but if you don't know what it
means, don't worry about it.
It would be a good exercise for you to insert some code to display the value of the formal parameter
following the recursive call to see that the value of the formal variable is still available when we
return from each recursive call. This code would be inserted immediately after line 18.
You should spend the time necessary to completely understand this program before continuing on to
the next example program.
A RECURSIVE FUNCTION
Example program ------> e_c18_p4.ada
Examine the program named e_c18_p4.ada for an example of a recursive function, which is actually
no different than a recursive procedure. This program is used to illustrate two other Ada concepts,
neither of which is new to you, but both of which contain valuable insights for you. The first is the
partial declaration which will be discussed at the outset, and the other is a return to exceptions which
will be deferred for a couple of paragraphs.
THE PARTIAL DECLARATION
Ada requires that you define everything prior to its use, and this rule is never broken. Suppose you
wished to have a program with two procedures, two functions, or even a procedure and a function,
calling each other recursively. If A were declared first, it could be called by B, but A could not call B
because it would not be defined by the time A was elaborated, and we cannot break the rule of
defining everything prior to its use. This is more properly called linear declaration. Ada gets around
this by allowing a partial declaration of a type, procedure, function, or package. If you remember, we
used the partial declaration with respect to a record when we studied the access type variable, so this
concept is not entirely new to you. It is also proper to refer to the partial declaration as a function
specification.
In the present example program, we desire to place the functions in the program in the order shown,
for no good reason other than to illustrate that it can be done, but we have the problem of linear
declaration because Factorial calls Factorial_Possible and they are in the wrong order. The partial
declaration in line 12 tells the system that the function Factorial_Possible will be defined later and
what its characteristics will be, because the formal parameter is defined along with its return type.
The function Factorial is then completely defined, and it can call the other function because it has
the definition of its interface. We then have the complete definition of the function
Factorial_Possible and we have accomplished our desired goals, while meeting the requirements of
linear declaration.
NOW FOR THE RECURSIVE FUNCTION
Assuming you are familiar with the factorial function which is a part of higher mathematics, we will
continue with the program description. Since Factorial(N) is the same as N times Factorial(N-1), we
can calculate the factorial of any number using a recursive technique. We continue to recurse until we
reach a point where we need the value of Factorial(1), which we define as 1, and begin going back
up the recursive chain, each time multiplying by the value with which we entered into that particular
recursive call. By defining the value of Factorial(0) as 1, and making it illegal to take the factorial of
any negative number, we can now write the complete program. Most of the program involves
checking limits and outputting messages, but the actual work is done by line 27 which is the recursive
call as defined earlier in this paragraph.
EXTRA CHECKS ARE DONE
The program should not be at all difficult for you to follow and understand, so you will be left on
your own to study it. A few comments on style should be mentioned, however. The function
Factorial calls the other function to verify that the value is factoriable before attempting to do so, and
the main program, in lines 44 through 54, checks the value before calling the function to attempt to
factorialize the number. A redundant check like this is not necessarily bad, because the procedure was
written to be all inclusive, and the main program may wish to do something entirely different than
that dictated by the procedure. In lines 58 through 63 however, the main program accepts the default
error handling provided by the procedure, by calling the procedure without first checking the data.
Compile and run this program, observing the various messages output depending on which portion of
the program handled the errors. Note carefully that the program is not meant to be an illustration of
good programming style, only as an illustration of a few things that can be done using Ada.
ANOTHER LOOK AT EXCEPTIONS
The last program was unusual because it has the ability to illustrate three of the four standard
exceptions defined by the Ada system. They can be illustrated as follows;
G Program_Error - Remove the return from line 27 and you will drop out of the bottom of the
function. (Actually, a validated compiler will generate a compile error.)
G Constraint_Error - Change the type of the formal parameter in line 14 to POSITIVE, which
covers the range of all numbers legal to compute a factorial for. Then call the function with -1
as a parameter.
G Storage_Error - Call Factorial(100000). The stack should overflow prior to completing the
required number of recursions.
A FUNCTION RETURNING AN ARRAY
Example program ------> e_c18_p5.ada
The example program named e_c18_p5.ada will give you an example of a function that returns more
than a single scalar variable, in fact, it returns an entire array of INTEGER type variables. The
program itself is extremely simple, the only thing that is different from most functions is the type of
return listed in line 12, and the actual return in line 18. These must, of course, agree in type or a type
mismatch error will be issued during compilation.
Using the technique given here, there is no reason why a function cannot return a record, or even an
array of records, as long as the types are correctly defined and they agree when used.
THE INLINE pragma
A pragma is a compiler directive, as we have mentioned previously, and the INLINE pragma tells
the compiler to expand the called subprogram body and insert it into the calling program for each
call. Since the code is inserted inline, there is no calling sequence and therefore no time wasted in
setting up subprogram linkage, but there is a separate section of code for each invocation of the
subprogram. The result will usually be a larger but faster program. Use of this pragma is illustrated
as follows;
pragma INLINE(subprogram1, subprogram2, ... );
This line is inserted in the declarative part of the compilation unit, following the subprogram
specifications. By definition, the meaning of a subprogram is not affected by the pragma.
OPERATOR OVERLOADING AND THE "use" CLAUSE
Example program ------> e_c18_p6.ada
Operator overloading has been mentioned in this tutorial before, but it is necessary to cover a little
more ground on this very important topic, and we will use e_c18_p6.ada as a vehicle for discussion.
We define a package specification in lines 2 through 18 named Shape with a record type named
BOX defined within the package. The subprograms Make_A_Box and Print_Box provide us with
the ability to generate and display any variable of the BOX type, and we provide three overloaded
operators for use with this type. Two of the subprograms overload the + operator for use with
variables of this type, and the other provides us with a multiply capability. Many more overloadings
could be defined if desired, but these three illustrate the technique.
The body for the package is given in lines 22 through 75. The overloaded operations make little sense
in the real world, since adding two boxes would not usually be done by simply adding the size of all
three dimensions. However, we are far more interested in the technique of overloading operators, so
the interface is what really matters here. The implementation should be simple for you to study on
your own.
THE CALLING PROGRAM
The calling program is definitely the most interesting part of this example, beginning with line 80
where we have a use type construct which is new to us. A short digression is in order at this point to
explain what this does.
Many experienced Ada programmers feel that the use statement should never be used in an Ada
program because it hides the source of any subprogram calls or type definitions. For this reason many
projects outlaw its use. It seems reasonable to this author, that Ada.Text_IO should be a permitted
violation of this rule, but that is yet another digression so we will not consider it further. In order for
the overloaded operators to be available for use as infix operators, as illustrated in lines 94 through 97
of this example program, the use clause must be included. If it is not included, line 97 would need to
be written as illustrated in line 99, a very ugly and difficult to read notation. The use of the use type
construct in line 80 makes the overloaded operators available for use as infix operators, but does not
permit the use of any other subprograms from the Shape package without the package prefix. This
gives the ease of readability, and the extra safety desired by some project leaders.
The package name is required when defining variables in lines 84 and 85, and when calling
subprograms that are not operator overloads, such as illustrated in lines 89 through 99. Note that line
97 calculates the result we expect because the multiplication is done prior to the addition. It is not
possible to change the order of precedence when overloading operators.
PROGRAMMING EXERCISES
1. As suggested earlier, include an output statement in e_c18_p3.ada to display the value of
Value after the recursive call to see the recursion come back up through the chain of recursive
calls.(Solution)
2. Write a recursive program with two procedures named Increment and Display which call
each other and increment a variable, initialized to 1, until it reaches a count of 8.(Solution)
Advance to Chapter 19
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 19
ADVANCED ARRAY TOPICS
Example program ------> e_c19_p1.ada
Examine the file named e_c19_p1.ada for an example of an unconstrained array type illustrated in
line 7. The box (<>), as it is called, refers to something that must be filled in later, and is a construct
that will be used in many other places in Ada. Note that the index type in line 7 is POSITIVE, a fact
that will have a bearing on its use later.
USE OF THE UNCONSTRAINED ARRAY TYPE
In line 9, we declare an array constant of type MY_ARRAY and we assign values to the constants by
use of the positional aggregate. Since we do not give the range of the indices of the array, the system
will assign them, and in this case will use a range of 1 through 4, because of the use of the type
POSITIVE for the index type. If we would have used type INTEGER for the index, the selected
range would have been -2,147,483,648 through -2,147,483,645, which is the lower end of the
INTEGER range, unless your implementation used a different lower limit for INTEGER. Careful
selection of the array index type is important. In line 10, we declare another constant array, but this
time we use a named aggregate and the system does not get the opportunity to pick the range, we pick
it explicitly ourselves. The indices do not have to be named in consecutive order as is done here, but
all values must be given.
We declare an uninitialized variable array in line 13, which covers the range of 1 through 12, and an
initialized variable array in line 14, which uses the positional aggregate method of initialization. The
others portion will fill in positions 9, 10, and 11 with the value of 17. The others construct used in
the array aggregate is new in Ada 95. It can only be used with a constrained array and it must be last
in the list.
A VERY FLEXIBLE FUNCTION
The function in lines 17 through 27 appears to use an unconstrained array for its formal parameter
variable, and it does, but with each use of the function, the formal parameter array is constrained to
the limits of the actual variable. When the function is called in line 33 of the program with My_List
as the actual array variable, the range of My_List, 1 through 12, is used for the range of the formal
parameter in the function. This makes it possible to use the array attributes within the function, in the
manner shown, such that they are dependent on which array is used for the actual parameter. When
Stuff is used for the actual parameter, the formal parameter will have a range of 4 through 11 within
the function. The function can therefore be written in such a way that it has flexibility built into it,
even though strong type checking continues to be done, since the function cannot be used with a
different unconstrained type if we had one in this example program. Note that all array attributes are
available here as they are with any array.
A FEW NOTES ABOUT THIS TYPE
All elements of every variable of type MY_ARRAY will be of type INTEGER, and all indices will
be of subtype POSITIVE including the range limits. The variables named My_List and Stuff are of
the same type with different limits, so the slice can be used with them. After you study this program,
compile and execute it so you can observe the output.
AN ARRAY WITH AN ENUMERATED INDEX
Example program ------> e_c19_p2.ada
Examine the program named e_c19_p2.ada for an example of an array with an enumerated variable
for an index. Since the index of an array may be any discrete type, and an enumerated type is
discrete, it can be used for an array index according to the Ada standard.
We define an enumerated type named DAY, that includes the days of the week as elements, then
declare an array using the type DAY for the index. It should be apparent to you that this is an
anonymous array type, the use of which is discouraged in a significant program, but should cause no
type conversion problems in a simple program such as this one. Finally, we declare two other simple
variables for later use and begin the executable part of the program.
We use a loop to assign 8.0 hours to each day from MON through FRI, then assign 4.0 hours to
SAT, and 0.0 hours to SUN. Finally we sum up the hours for the week and display them on the
monitor. It is a very simple program, but it illustrates a very useful construct in Ada. For that reason,
you should spend enough time studying it until you thoroughly understand it. Be sure to compile and
execute e_c19_p2.ada.
ARRAY OPERATORS
Example program ------> e_c19_p3.ada
Some of the operators discussed earlier in this tutorial are available for use with arrays. They operate
on each element of the array as if the operation were done in a loop. The example program e_c19_p3.
ada will illustrate the use of a few of these operations.
Lines 19 through 24 illustrate the logical operators being used with entire arrays. The arrays are
compared element by element, and as soon as a difference is found in two corresponding elements,
the comparison result of these two elements is returned as the overall comparison result. If all
corresponding elements are equal, the result of the comparison is equal.
Note that it is legal and sometimes very useful to use an array of records. Arrays of records can be
compared for equality or inequality, but not for the other four operators ( >, >=, <, <= ) because they
are illegal for use with individual record elements. Some thought on your part would have led to this
conclusion without us explicitly stating it. Many such combinations of operations are possible with
Ada. It will be up to you to try a few yourself, as you need them, because there are too many
permutations for us to delineate all of them.
ARITHMETIC ARRAY OPERATORS
The operators illustrated in comments in lines 26 through 31 are not available as a part of Ada, but
they can be made available to your programs by use of operator overloading. This will be illustrated
in the next example program.
BOOLEAN ARRAY OPERATORS
Two of the BOOLEAN arrays are assigned some values using positional aggregates in lines 33 and
34, then used in lines 36 through 44 as complete arrays. The logical operators are used in much the
same way that the comparison operators were used previously in this file. Note that these operators
result in another array, each element being the result of the logical operation of the corresponding
elements in the compared arrays. The comparison operators are available with boolean arrays in a
manner similar to that with other scalar arrays, only a single BOOLEAN type result value is
returned. Compile and run this program after you understand the new material presented.
HOW TO WRITE THE ARITHMETIC ARRAY OPERATORS
Example program ------> e_c19_p4.ada
The example program named e_c19_p4.ada illustrates how to write the arithmetic array operators.
This is actually an overloading of the usual arithmetic operators. Overloading these operators is
nothing new, you have been doing it all through this tutorial, because the plus sign, for example, has
been available for adding integer types, as well as fixed and floating point types. There is no reason
the plus sign could not be used to add arrays, element by element, and that is exactly what we will
illustrate here.
The "+" function is listed in lines 11 through 18 and is a very simple function, with no difficult code.
The name of the function is the unusual thing about this function, its name being "+". This is the
usual Ada method of overloading operators which we have illustrated before in this tutorial. After the
function is defined, it is called by using an infix notation, as illustrated in line 34 of the program
where all 6 elements of Group1 are added to the corresponding elements of Group2, and the 6
results being assigned to the 6 elements of Crowd. The results of the addition are displayed in lines
35 through 40 to show that all elements were summed.
In a similar manner, the function named "mod" is used to provide an infix notation for the modulo
operator, and is called in line 45. The other four arithmetic operators are not defined here since they
are so similar to the two which are illustrated. The overloading of the mod operator in this way
should indicate to you why it is important to think of mod as an operator rather than a subprogram
call.
OVERLOADING RULES
There are two rules which must be considered when overloading operators, the first being that you
are permitted as many overloadings for a given operator as you desire, provided that the parameters
of the inputs and result have unique types for each of the overloadings. This is because the system
uses the types of the parameters and the type of the result to determine which overloading you wish
to use each time you use the operator. This implies, correctly, that you cannot redefine the use of an
existing operator for a given type.
The second rule is simple. You can overload existing Ada operators, but you cannot define an infix
operator that is not predefined in Ada. Be sure to compile and execute this program.
UNARY OPERATOR OVERLOADING
Example program ------> e_c19_p5.ada
Examine the program named e_c19_p5.ada for an example of overloading the unary operators "+"
and "-". The function in lines 27 through 30 overloads the "+" operator for the array defined and is of
little interest since nothing is really accomplished here. The function in lines 32 through 39 overloads
the "-" operator and negates each element of the array. It may seem silly to even bother with
overloading the "+" operator, but it is illustrated here and can be used to advantage when we come to
a study of generic packages. It may be necessary to allow use of such a seemingly silly construct in
order to write a general purpose generic package.
Be sure to compile and run this program and see that the illustrated overloadings work as we have
stated.
OPERATOR HIDING
In all of the overloadings we have examined, we were careful to use overloadings that introduced
new type combinations so there was never an instance of hiding another subprogram. If the type
combinations are already overloaded in a global manner when a further overloading is declared at a
lower level of nesting, the subprogram called by the global combination will be hidden and the nested
subprogram will be called each time. Even though care must be exercised when overloading
operators or identifiers, do not fear use of this powerful technique made available to you by the
designers of Ada.
PROGRAMMING EXERCISES
1. Modify the program named e_c19_p2.ada to output the name of each day as an enumerated
output along with the number of hours worked each day.(Solution)
2. Write functions for any two of the remaining four arithmetic operators in the example program
named e_c19_p4.ada, and test the two new functions.(Solution)
Advance to Chapter 20
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 20
ADVANCED RECORD TOPICS
A RECORD WITH A DISCRIMINANT
Example program ------> e_c20_p1.ada
Examine the file named e_c20_p1.ada for our first example of a record with a discriminant. It will
take a little time and study before we get to the discriminant and what it does, but we will take it a
step at a time.
We begin by defining an unconstrained two dimensional array type in line 7 of the declaration part,
and an unconstrained one dimensional array type in line 10. Next we define a record type, beginning
in line 12, with a discriminant, the discriminant being a variable named List_Size which is of type
POSITIVE. The record is composed of four fields, each of which is defined in part by the
discriminant. The variable named Matrix is a square array whose size is given by the value of the
discriminant, while Elements is initialized to the square of the discriminant. Likewise, the other two
fields are defined as a function of the discriminant. Keep in mind that the discriminant does not yet
have a value, it is only used as a part of the pattern of the record.
For later use, we define a derived type in line 20, and a subtype in line 22. The subtype is defined as
being of type STUFF but with the discriminant being fixed at 5. We will have more to say about
these two types later.
WE NEED TO DEFINE SOME DATA NOW
In line 24, we declare a variable named Data_Store to be of type STUFF with the discriminant set
equal to 5. Therefore, the Matrix variable which is a part of the record named Data_Store has two
subscripts, and both cover a range of 1 through 5. The variable named Elements will be initialized to
the square of 5, and the other fields will likewise be defined. The variable Big_Store will have larger
arrays, and the value of its subfield, named Elements, will be initialized to a value of the square of
12. Since these two variables have different numbers of elements, they are not assignment
compatible, nor can they be compared for equality or inequality.
The variable Extra_Store is declared as being of type ANOTHER_STUFF with a discriminant of 5,
but since the types are different, this variable is not assignment compatible with the first variable
named Data_Store. More_Store is declared as being of type STUFF with a discriminant of 5, so it
is assignment compatible with Data_Store. Five_Store, because it is a subtype of STUFF, and its
discriminant is 5, as defined in the subtype declaration, is assignment compatible with Data_Store.
Finally, it should be clear that the last example, Name_Store, is assignment compatible with
Data_Store, since its only difference is that it uses the named method of discriminant selection. This
is a hint to you that additional discriminants can be used, and there is actually no limit to the number
that can be a part of a discriminated record type. We will have an example program soon that has
three discriminants.
WHO IS ASSIGNMENT COMPATIBLE WITH WHO?
As mentioned before, the variables named Data_Store, More_Store, Five_Store, and Name_Store,
are all of the same type and can be freely assigned to each other. They can also be compared for
equality or inequality with each other. The other variables are of different types and cannot be
assigned as a complete record to each other, or compared for equality or inequality.
The discriminant, once declared, is considered to be a constant, and cannot be modified. The
discriminant of the variable Data_Store is accessed in the program as Data_Store.List_Size for
purposes of reading it. The executable part of the program should be clear. One of the declared
Matrix variables is assigned values using the RANGE attribute for the loop limits. The entire record
is then assigned to some additional record variables, and a single data point is displayed as an
example.
When you understand the program, compile and execute it to prove to yourself that it works as
shown. Notice that, as always, the elements of the record cannot be anonymous types but must be
named.
HOW DO WE USE THE NEW RECORDS?
Example program ------> e_c20_p2.ada
Examine the example program named e_c20_p2.ada for a few examples of how to use the
discriminated record. The type declarations are identical to the last program, but only two records are
declared this time, Data_Store and Big_Store, which are of different types because they have
different discriminants.
The declaration part of the program has a function declaration and a procedure declaration added to it
in this program. If you look closely, you will see that the type used for the formal variable in both
subprograms is of the record type STUFF, but there is no discriminant defined for either. These are
unconstrained records and add flexibility to the use of subprograms. The loops within the
subprograms use limits that are dependent upon the limits of the actual type as defined in the calling
program, and the subprograms can therefore be used for any record variable of type STUFF
regardless of the value of the discriminant.
USE OF THE UNCONSTRAINED SUBPROGRAMS
The nested loop in lines 45 through 50, assigns the elements contained in the array variable
Data_Store.Matrix to a multiplication table for later use. In line 52, we call the procedure
Set_To_Ones with the record Big_Store to set all of its Matrix values to 1. Finally, we display the
sums of all of the elements by calling the function Add_Elements once for each record. Even though
the records are actually of different types, the function works correctly with both, because of the
flexibility built into the function itself. Note that even though the record is unconstrained in each
subprogram, it is constrained when the subprogram is called since the discriminant is constrained to
the value of the actual parameter in the call.
Be sure to compile and execute this program and study the output until you are sure you understand
the results.
A VARIABLE DISCRIMINANT
Example program ------> e_c20_p3.ada
Examine the program named e_c20_p3.ada for an example of a discriminant that can be changed
dynamically. This program is nearly identical to the last example program, but there are two very
small changes. The discriminant is initialized to a value of 2 in line 14, which will be used for the
discriminant if none is given in the variable declaration. The second change is illustrated in line 24,
where the variable Var_Store is declared to be of type STUFF, and is defaulted to the initialization
value of 2 for its discriminant. There is one other property that the variable named Var_Store has
acquired, and that is the ability to have its discriminant changed to any legal value during execution
of the program.
The two variables named Data_Store and Big_Store have their discriminants fixed at 5 and 12
respectively and cannot be changed during program execution.
HOW DO WE CHANGE THE DISCRIMINANT?
The discriminant can only be changed by changing the entire record in a single statement as is
illustrated in line 55 where the entire record named Data_Store, with a discriminant of 5, is assigned
to the variable Var_Store. The variable Var_Store can then be used anywhere it is legal to use a
record of discriminant 5 as shown in lines 56 through 62. Note that prior to the assignment in line 55
the variable Var_Store can be used as a record with its discriminant set to 2 because that is the value
of the default.
In line 66, the variable Var_Store is assigned the entire record of Big_Store which effectively
changes it into a record variable with a discriminant of 12. The fact that it is changed is evidenced by
the output which you can see after you compile and execute this program. Note that all of the values
contained in Big_Store are copied into Var_Store. Be sure to compile and execute this program.
A MULTIPLE DISCRIMINANT
Example program ------> e_c20_p4.ada
Examining the example program named e_c20_p4.ada gives you an example of how to use a multiple
discriminant in a record type. The first difference is in lines 14 through 16 where three discriminants
are defined for the record type, and the next big difference is in lines 24 through 30 where 5 variables
are declared with this type illustrating the various kinds of discriminant initialization described in this
chapter.
Since we have studied all of these in the last few example programs, we will not elaborate on them,
except to mention that the variable defined in line 30, named Variable, can be assigned the value of
any of the other variables. It will then have the entire set of discriminants assigned to it that the
assignment variable possesses and it will be assigned all of the current values stored in the source
record. This is exactly the same as what was illustrated in the last program.
Be sure to compile and run this program after you understand the concepts it is meant to convey to
you.
A VARIANT RECORD
Example program ------> e_c20_p5.ada
Examine the file named e_c20_p5.ada for our first example of a variant record. A variant record must
have a discriminant, by definition, since the discriminant will define which of the various variants
each record variable will be composed of. If you are a Pascal programmer you will find the variant
record to be much more confining than in Pascal where you can change the variant at will. If you
remember that Ada is a very strongly typed language, and will accept no deviation from its defined
standard in order to detect errors inadvertently introduced by the programmer, you will appreciate the
terse definition and restrictions.
The discriminant for our example record is declared in line 5 as an enumerated type with four
allowable values. A variable named Engine is declared, which is of type POWER, which will be
used as the discriminant and two variables are declared as the first part of the record. The variant part
of the record begins in line 11 with the reserved word case followed by the name of the discriminant
variable, which is Engine in this example. The four variants are declared in much the same way as a
normal case statement with variable declarations in place of the executable statements. There must be
a clause listed for each possible value of the discriminant, and any number of variables may be
defined for each, including none. If no variables are declared for a case, the reserved word null is
used to indicate to the compiler that you really mean to include no variables there.
If there is a variant to the record, it must be the last part of the record with all common variables
declared first. One or more of the variant parts of the record can have a variant part itself, provided it
is the last part of the variant part. There is no defined limit to the nesting.
HOW DO WE USE THE VARIANT RECORD?
In line 20, we declare the variable Ford to be a record of type VEHICLE, and constrain it to be the
GAS variant of the record. Because the Ford variable is constrained to the GAS variant, it can only
use the variable declared as part of the GAS variant, and of course the two common variables. It
would be illegal to assign data to the fields of the Ford variable which are declared in the other
variants of the record type. Moreover, since the variable Ford has been assigned the discriminant
value of GAS, it can never be changed, but is a constant. Likewise, the variable named Truck will
always be a record of type VEHICLE with the variant DIESEL because that is what it is declared
with. The same is true of the other two variables declared in lines 22 and 23. We will see in the next
program, that it is possible to declare a variable in such a way that it can be modified dynamically to
any of the variants during program execution.
HOW DO WE ASSIGN VALUES TO THE VARIABLES?
Lines 27 through 29 should be familiar to you since this is the method used to assign values to a
record with no variant, which we studied earlier. Line 31 illustrates value assignment by using a
positional aggregate notation, and line 34 illustrates assignment of values using the named aggregate
notation. In both of these cases, all four fields must be named even if some are not changed. Even the
invariant discriminant must be included in the aggregate, which seems to be somewhat of a nuisance,
since it is a constant. The reasons for these last two rules are probably well founded and have to do
with ease of writing a compiler, which is certainly no small job.
The statements in lines 37 through 40 assign values to each of the subfields of the record variable
named Stanley, and the mixed aggregate assignment is illustrated in line 42, where all five variables
are mentioned even though the discriminant is a constant. Finally, the Schwinn and Truck variables
are assigned values in lines 45 through 51. Compile and execute this program to assure yourself that
your compiler will indeed compile it correctly.
A VARIABLE VARIANT RECORD
Example program ------> e_c20_p6.ada
Examine the program named e_c20_p6.ada for an example of a variant record in which we can
change the variant dynamically during program execution. A major difference here is that the
discriminant is defaulted to the value listed in line 7, namely the value of NONE. If no variant is
declared, it is defaulted to NONE in the declarations as is done in line 20, where three variable
records are declared using the default value of the discriminant. The variable Stanley is once again
declared to be of the variant STEAM, and this will remain constant throughout the execution of the
program, because any variable declared with a discriminant value cannot have the value of its
discriminant changed dynamically but is a constant, although the individual elements of the record
can be changed.
NOW TO USE SOME OF THE NEW VARIABLES
In line 25, the variable Ford is assigned data such that it is of the GAS variant, using the positional
aggregate notation, and is redefined in the next statement to be of the DIESEL variant, by using the
mixed aggregate notation. This is done to illustrate to you that it is possible to change the variant of a
variable if it was declared with the default variant. In line 28, the variable Truck is assigned the
DIESEL variant with the positional aggregate notation, and two of the fields are changed in the next
two statements.
Any of the fields can be changed individually with the exception of the discriminant variable, which
can only be changed by use of an aggregate in which all values are listed. Remember that this is the
aggravating part of this construct, that all of the fields must be mentioned in every record aggregate.
Even though you can change individual values of the record, you are limited to using those variables
that are part of the current variant. Pascal allows you to use variable names of other variants but Ada
will not permit this. You are permitted to use the positional, named, or mixed aggregate notation,
however. Be sure to compile and execute this program after you understand the concepts.
OPERATOR OVERLOADING
Example program ------> e_c20_p7.ada
The example program named e_c20_p7.ada could be placed in several different places in this tutorial,
because it doesn't really fit anywhere too well. Since one of the most advantageous places to use
operator overloading is with record type variables, this seemed like a good place for it. The program
itself is very simple, but the concept is potentially very powerful.
We first define a record composed of three simple variables all being of type INTEGER, and use this
definition to declare three variables of this type. The next declaration is of a function named "+",
which we can define to do anything we wish it to do. In this case we input two variables of the record
type THREE_INTS and return one record of the same type. Within the function, we add all three
fields of one variable to the corresponding fields of the other variable, and return the record
consisting of the sum of the two input records. To make it even more convenient, Ada allows you to
use the overloaded operator in an infix notation as illustrated in line number 30 of the program where
two records are added together and assigned to the record variable named Sum. This line is actually a
call to the function we defined earlier and named "+".
It could be a bit confusing if you consider the addition in line 24 where the usual addition operator is
used. Ada will decide which + operator to use based on the type of the constants or variables to be
added together. If you were to try to use this operator on a record of some other type, such as the
VEHICLE record from the last program, the system would generate a type error during compilation.
As mentioned previously, it is only possible to overload existing operators. You cannot define a new
operator, such as &=, and use it for some operation.
Recall the chapter named Advanced Array Topics where we overloaded the "+" operator in the
example program named e_c19_p4.ada. Both overloadings of this operator could be included in a
single program and the system would be able to find the correct overloading for each use by checking
the types used. Be sure to compile and execute this program even though it has no output.
PROGRAMMING EXERCISES
1. Add some output statements to e_c20_p5.ada to display some of the results.(Solution)
2. Try to change the variant of Stanley in the same way we changed the variant of Ford in the
example program named e_c20_p6.ada to see what kind of error messages you get.(Solution)
3. Add another overloading function to e_c20_p7.ada which uses the "+" operator to add all six
elements of two records together and return a single INTEGER type variable. The system can
tell which overloading you wish to use by the return type associated with the function call.
(Solution)
Advance to Chapter 21
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 21
ADVANCED PACKAGES & PRIVATE TYPES
THIS IS FOR LARGE PROJECTS
The material in this chapter is used for large projects when there are several programmers working
together as a team. In such a case, there is a demand for good communication between the various
programmers, regardless of what programming language is used. If Ada is the language chosen for
the project, and if the principles set down in this chapter are carefully followed, the amount of
communication required can be minimized. Of course, these techniques can be used by a lone
programmer to improve the quality of his code also, especially if it is a large program.
TWO GOOD PROGRAMMERS
In this chapter, we will assume we have a project being developed by two programmers, and that they
are both sharp and well versed in how to develop quality software. We will follow along with the
example programs as they develop a simple system to add or subtract groups of three numbers. One
has been assigned the responsibility to write a package that the other programmer can use to add or
subtract groups of three numbers of arbitrary structure.
This is a trivial problem, of course, but will illustrate the concept of information hiding, which is so
necessary on a large project.
THE FIRST SOLUTION
Example program ------> e_c21_p1.ada
The example file named e_c21_p1.ada illustrates the first attempt to solve the problem, however it
does not practice any information hiding. Lines 1 through 69 represent the work of the package
writer, and lines 74 through 101 are written by the user of the package. The package writer used a
record to store the three numbers and because he declared the record in the specification package, he
gave the user full access to the record for his own use.
The package writer, by providing three functions and a procedure, gave the user the ability to add the
corresponding elements, subtract the corresponding elements, and two additional capabilities. The
function named Build_Structure allows the user to build a new structure given the individual
components, and the procedure named Decompose, allows the user to separate the elements into their
respective values. We might add that this package is a result of a meeting between the two
programmers where they agreed on what the system must do and what the interface between them
must be. The package specification is intended to be the only information needed to define the
interface between the programmers. Notice the comments in lines 3 through 7 that define clearly and
completely what the package does.
The user wished to change one value of the variable My_Data, and since he had the entire structure
available, he cheated in line 99 by circumventing the available structure handling method provided by
the package writer. He feels he saved a few instructions and a little execution time, so the little bit of
cheating is justified. Line 99 is perfectly legal, but could cause a problem in this instance due to the
immense size of the project. Recall that he had previously agreed to use only the functionality
provided by the package writer because the package writer is supplying the same package to another
large project and must maintain conformity with both users.
A PROBLEM DEVELOPS
The package writer, for reasons of efficiency and conformity to the other project, decides to change
the way he stores the data from a three element record, to a three element array. With this
modification in place and tested, the user suddenly finds that his program will no longer compile,
even though nothing has been changed in his program. The only problem is with line 99, which has
been working just fine for months now, but as we stated earlier, is actually cheating on the interface
agreed upon by the user and the package writer. Information hiding would have prevented this
problem, which in a real world system could cause hundreds of compiler errors following a
seemingly minor change in one of the packages.
In this case, the user cheated and resulted ultimately in a rather large problem. We need to prevent
him from cheating. Of course we could make a project rule that says "no cheating", but we will
shortly define a way to let the compiler enforce the rule for us. Using the word "cheating" is simply to
enhance the story. Actually, it is more of an oversight on the part of the user. While debugging after a
14 hour day of programming, it is easy to forget the previous agreement on the interface and use
some of the data directly as is done in line 99. We need a way for the compiler to remind us not to do
that.
It should be obvious to you that in a real-life large system, the called package and the calling package
would not be in the same file, but would be separately compiled. They were combined to make it
easier for you to compile and execute, which you should do at this point.
A BETTER WAY, THE PRIVATE TYPE
Example program ------> e_c21_p2.ada
Examine the program named e_c21_p2.ada for an example of a private type which forces the user to
follow the rules. In the package specification, the type DATA_STRUCTURE is defined to be of
type private, a reserved word in Ada, which means that the type name is available for the user's use
in declaring variables of this type, but the structure of the type is not available to him. The actual type
is defined at the end of the package specification following another use of the reserved word private.
Because the type is private, the user can make no use of the structure, even though he may be able to
see the structure, as you can at this moment. If the package writer wished to do so, he could actually
remove the type definition from the listing and give the resulting listing to the user, but the fact that it
is private is as good as physically hiding it from the user.
Because the type is private, the user has the ability to compare two variables of that type for either
equality or inequality, and he can assign another variable or constant of that type to a variable of the
same type. No other operations on a variable of this private type are available to him, except for those
specifically defined in the package specification itself.
NO TRICKS THIS TIME
The trick, in line 99 of the last program is therefore illegal, and will result in a compile error. If the
user needs the ability to do some operation on this type variable, he must ask the package writer to
provide it for him. The package writer has the ability to do anything with the components of the
structure within the package itself, and define any new procedures or functions necessary for the
intelligent use of the type. In order for the user to add 13 to one member, it is now necessary to
declare a temporary record variable, load it with all fields, and add the entire temporary variable of
the type DATA_STRUCTURE to the variable. This is illustrated in lines 106 and 107. It is rather
obvious that these two statements could be combined and the variable named Temp not needed, but
this was done for clarity.
WHERE IS THIS TYPE AVAILABLE?
Following the partial declaration of the record type in line 11, it is available for use in the remainder
of the specification package, and throughout the entire package body. It is important to remember that
the private type is available in these areas just as if it were not private. It is only treated in a different
manner with respect to the using program. We actually have two different views of the private data,
a partial view available to any outside calling program, and a full view available to the
implementation or body of the defining class. Actually, only the partial view is available in the
specification prior to the private section, and the full view is available within the private section
following the complete definition of the record.
The remaining details of the program should be simple for you to understand, compile and execute.
Note that the output is identical to that from the previous example program.
A NEW PACKAGE
Example program ------> e_c21_p3.ada
The package writer, for his own mysterious reasons, decides to change the package to use a different
structure. The example program named e_c21_p3.ada does exactly that. The only change from
e_c21_p2.ada is the method of defining the structure, and in this case an array is used. Since the user
was forced to follow the rules, his program will run with no modifications. See if you can find
anything you can do in the main program using the structure defined in either program that cannot be
done in the other. According to the design of Ada, it should be impossible for you to do so.
Notice that the internal workings of the package body are significantly different to reflect the
difference in the two data structures, but externally, you cannot tell the difference. Even though the
package was changed significantly, none of the users will see a difference and there will be no
incompatibility problems. Notice that the main program in this example program is identical to the
main program in the last example program. Be sure to compile and execute this program.
THE LIMITED PRIVATE TYPE
Example program ------> e_c21_p4.ada
Examine the program named e_c21_p4.ada for an example of a limited private type. The biggest
difference between this program and the previous one is the addition of the reserved word limited in
line 11 which declares the type DATA_STRUCTURE to be of type limited private. This gives the
package writer even more control over what the user can do with a variable of this type. There are no
operations the user can perform on data of this type except for those explicitly spelled out by the
package writer in the form of procedures and functions.
If the user were to declare two variables of this type, the compiler would not allow him to compare
them for equality or inequality, nor could he assign one variable to the other. In fact, he could not
even declare a variable of this type and initialize it to some value, because that would require an
implied assignment statement. He cannot declare a constant of this type either since that would also
require an assignment statement. The package writer provided a procedure named Assign_Struct,
and a function named Compare so the user could perform these operations. The program itself,
beginning in line 95, is a bit messy because of the procedure calls required to do the assignments.
You are surely asking, "why should we go to all of the trouble to use the limited private type?"
WHY USE LIMITED TYPES?
It is conceivable that you, as the package writer, do not wish to give the user the ability to compare or
assign variables of the type in question because of the nature of the project. You may be working on
some form of a data encryption system, or a limited access file management system, and you wish to
be sure that all users are positively locked out of access to certain files. The lack of compares and
assignments would make it impossible for a user to access the lower levels of your system. This is a
possible use for the limited type or the limited private type, but there is a much more important need
for it. Compile and execute this program and we will discuss the reason for using a limited type.
OVERLOADING THE "=" OPERATOR
The equals operator is defined for the private type, so it cannot be overloaded in the same manner
that we have been overloading operators in this tutorial. The limited private type does not have the
equality operator available for use, so it can be overloaded in any manner the package writer decrees.
Suppose for example that you were using the dynamic string package, named e_c16_p3.ada, from
chapter 16 of this tutorial, and you wished to compare two strings for equality. If the strings were, for
example, 80 characters long, but they only had 31 characters in use at this time, an overloading of the
equality operator could provide a function that would compare only the 31 "active" characters of the
string, stopping short of the additional characters in the static string declaration. This would allow the
comparison to be done using an infix operator rather than a function call for the comparison.
Overloading the "=" operator is not permitted for any type except the limited private type or the
limited type in all of Ada.
Example program ------> e_c21_p5.ada
A very useful example is given in the example program named e_c21_p5.ada. The record is defined
to be of type limited, which means it has no assignment available and it has no comparison operators
for either equality or inequality. This was done on purpose so we could define our own comparison
operator which we do in line 20. Since the function "=" returns a BOOLEAN type, it also provides a
default comparison for inequality that returns a BOOLEAN and we cannot redefine the inequality
operator. If we would have defined the compare for equality operator to return any other type than
BOOLEAN, we would not have a compare for inequality automatically defined, and we could
overload the "/=" operator to return any type we wish it to.
This program is based on e_c18_p6.ada from chapter 18 which used the Make_A_Box function as
illustrated in lines 16 and 17, but this function could not be used here because we have no assignment
capability with the limited type. This means that we cannot assign the result to a variable of this
limited type. It was necessary to convert the function to a procedure so we could return a value
without an assignment in the calling program. Lines 88 and 89 are commented out because they had
to be rewritten as lines 91 and 92 which do not use an assignment operator in the calling program.
The big thing we gained here is that we are not required to use the default comparison operator. We
may wish to use some very strange means as a basis for equality, and use of the limited type will
permit us to do so.
You may think that it is very inefficient to write a program that requires a procedure or function call
to do anything at all, but that is not necessarily the case. If the compiler writer has done a good job, a
subprogram call can be very efficient, and a special purpose comparison could be much more
efficient than a general purpose comparison routine that is designed to handle all cases. Because a
general purpose routine is designed for flexibility, it may not do any particular comparison very
efficiently.
Be sure you compile and execute this program so you can verify that it does the same thing as the last
two example programs.
A PACKAGE WITHOUT A BODY
It is possible to declare a package specification without a corresponding body. That would be done if
you wished to declare types, variables, and constants that could be used by other packages and
subprograms, but included no executable code with the declarations. Programming exercise 3 will
illustrate this technique. It is never possible to declare a package body without a corresponding
package specification.
PROGRAMMING EXERCISES
1. Add a function to PRIVATE1.ADA named Compare_Sum, which compares the sum of the
three elements of one structure to the sum of the three elements of the second structure,
returning a TRUE if the sums are equal, and a FALSE otherwise. Add a few statements to the
main program to exercise this new function.(Solution)
2. Add the same function to PRIVATE2.ADA, and add a few statements to the main program to
test it.(Solution)
3. Write a package named Stuff that contains only a package specification. The package
specification should make the value of Pi (3.14159) and Two_Pi available as well as a DATE
record type. Include a short program to use the package.(Solution)
Advance to Chapter 22
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 22
OBJECT ORIENTED PROGRAMMING
In recent years, object oriented programming has seen a tremendous rise in popularity. It seems like
the term has been applied to every programming language and marketing scheme regardless of
whether it is actually a part of the product. There are probably very few persons that really
understand what it is and know how to properly use object oriented programming, but that will not
prevent us from digging in and learning how to use some of the technique in our programs. We will
devote the next two chapters to this subject.
It is beyond the scope of this tutorial to define and illustrate such topics as object oriented analysis
and object oriented design. There are lots of books available that cover those topics quite well, so we
will spend our time showing how the various constructs available in Ada 95 help us to build a more
reliable program.
The three terms that are usually applied to object oriented programming are encapsulation,
inheritance, and polymorphism or dynamic binding. We have already covered a good bit of
encapsulation, which can properly be called information hiding, in chapter 21 of this tutorial, and we
will defer our discussion of polymorphism until chapter 23. This leaves us with inheritance which we
will cover at this time.
INHERITANCE AND EXTENSION
Inheritance involves making a copy of some existing entity and adding to it to define an entity with
all of the properties of the original, but with added properties. Most other programming languages
approach this topic with the emphasis on the inheritance portion of the previous statement, with little
emphasis on the extension operation. Ada writers, however, place the emphasis on the extension
portion of that statement, so we will too. In this chapter we will illustrate how to begin with an entity
and extend it such that it has additional capability. As usual we will start with a simple example
program.
THE SIMPLEST INHERITANCE
Example program ------> e_c22_p1.ada
This program has nothing to do with object oriented programming as usually applied to programming
languages, but it illustrates simple extension of a few Ada types. Line 4 declares that the type
MY_INTEGER will have all of the properties of the type INTEGER but with a more limited range,
so it is actually inheriting all of its properties from the parent type. The same statement can be said
about the type MY_FLOAT in line 6. This inheritance is not very interesting however, so we will go
on to a more complex type.
We define a type in lines 8 through 13 named WOODEN_BOX which is composed of three simple
components and has several predefined operations which all records have, such as assignment,
compare for equality, and compare for inequality. We add another primitive operation, the ability to
add two objects of this type by overloading the + operator in line 15. In line 17, we derive a new type
named STEEL_BOX which will have all of the components and operations that its parent type has,
including the overloading of the + operator. The interesting part is in line 19 where we extend the
STEEL_BOX type by overloading the - operator. It should be clear that we have "inherited" all of
the functionality of the WOODEN_BOX type, and extended the operation of the STEEL_BOX by
the additional operator.
This is inheritance and extension, but it is very limited because we cannot add components to the
inherited capability, only operations. We will add components in example programs later in this
chapter, but this program fragment was given to illustrate a very basic form of inheritance.
MORE SIMPLE TYPE EXTENTION
Example program ------> e_c22_p2.ada
The example program e_c22_p2.ada illustrates a little more extension. It begins with a definition of a
very simple record named TRANSPORT, with two components and three explicitly declared
subprograms in addition to the operators that are generated automatically by the system such as
assignment and compare for equality. There is nothing magic or unusual about this record. It can be
used to define data and used according to the rules of Ada just like any other record we have studied
in this tutorial. In fact we will use it in the next example program.
The next record, named CAR, is a little more interesting because it is declared as a derived type of
the TRANSPORT record. This means that it has all of the components and functionality of its
parent. In fact, we can use this new type as if it has the three functions declared in lines 11 through 15
redefined with the first parameter of type CAR. All three of these subprograms can be called directly
with a variable of type CAR as the first parameter, without doing any type conversion on the
variable. We extend CAR by adding a function named Tire_Loading to give it more capability than
its parent type. This truly is inheritance and extension in the fullest sense of the terms, but we still do
not have the ability to add components to the new type, only functionality. We will be able to extend
components shortly.
The implementation in the package body is very simple and you should have no difficulty
understanding it with the knowledge of Ada that you have gained already. Note that the three
subprograms for the TRANSPORT type are available for the CAR type just as if they were
redefined here, but they are not, because they are inherited automatically from the parent type. All
operators and primitive subprograms are automatically inherited when we derive a new record from
an old record.
WHAT ARE THE PRIMITIVE OPERATIONS?
The primitive operations of a type are;
G all intrinsic operations predefined by the compiler such as assignment, compare for equality,
and attributes
G all primitive operations inherited from a parent, when using inheritance or derivation
G subprograms with one or more parameters and/or a return value of the type, which are
declared in the package specification along with the type declaration itself
The CAR type in the present example has some of each category. It has a few intrinsic operators such
as assignment. It inherits three operations from its parent class which are declared in lines 11 through
15. Finally, it has an operation that is unique to itself declared in line 21, the Tire_Loading function.
If the CAR type were inherited into another type, such as a SPORTS_CAR type, it would inherit all
four of the functions declared in lines 11 through 21 since they are all within its ancestor types.
USING THE TYPE EXTENSION PACKAGE
Example program ------> e_c22_p3.ada
Examine the file named e_c22_p3.ada for an example of using the two types we defined in the last
example program. You will notice that the package named Conveyance1 is with'ed and use'ed in
lines 3 and 4 to make them available for use in the program. We define two variables in lines 8 and 9,
one each of the two types which we will use in the program.
Lines 13 through 18 are rather simple, with nothing new, so you can study those on your own. The
same is true of lines 20 through 23, except that we will return to this group of statements shortly.
The first really interesting statement is given in line 25 where we call Set_Values with the first
parameter being of type CAR, even though we have no explicitly defined function with that type.
This proves that the system truly generated a function with the type CAR as the first parameter that is
identical to the same function with the type TRANSPORT as the first parameter. Since we didn't
write the code twice, we only have a single function to maintain that operates on both of the types,
but if we wanted to have them do different things, we could write a specification and a body for
CAR's own Set_Values function.
Type extension is therefore illustrated in this example program in line 25 as well as line 28, where the
function Get_Wheels is called, which was inherited from the parent type. Line 30 however,
illustrates type extension since we wrote a completely new function to retrieve the Tire_Loading
from CAR type variables. You will notice that because of the clear definition of Ada, it is impossible
to tell in this program which functions were inherited and which were extended. The TRANSPORT
type has no Tire_Loading function.
Object oriented programming includes the concept of information hiding which we are not practicing
with this example program. If you refer back to line 21, you will see that we are accessing the number
of Wheels on the Hummer directly. This is bad practice, because one of the most error prone
operations in computer programming is providing direct access to data over a large range. The
principle of information hiding requires that the data for an object be hidden within the object and
unavailable to the outside world as discussed in the previous chapter of this tutorial. We will
completely hide the data within the object in the next example program.
Be sure to compile and execute this program to be sure you understand what it does before going on
to the next example program.
TYPE EXTENSION WITH A PRIVATE SECTION
Example program ------> e_c22_p4.ada
The example program named e_c22_p4.ada is very similar to the previous program with the
exception that the two record types are declared to be private. This was done to prevent the user from
seeing into the objects and directly manipulating the contained data. However, with each
improvement we add to our code, there is the possibility that something else will not work as
conveniently, and there is a bit of a penalty to be paid here.
Because the TRANSPORT type is private, it does not provide full functionality to objects outside of
itself. In fact, the only operations it permits are assignment and compare for equality or inequality.
The three subprograms defined for the TRANSPORT type in lines 6 through 10 are not available to
be inherited into the CAR type because there is no indication that CAR inherits from TRANSPORT
at this point. It is necessary to declare them explicitly for the CAR type as is done in lines 14 through
18. It is also necessary to provide a full implementation for these three functions as is illustrated in
lines 58 through 74. It is clear that this form of inheritance is lacking in elegance.
As with all private types, the actual definitions are given within the private section in lines 21
through 29 where it is clear that CAR inherits all of TRANSPORT. This example is very similar to
the last example, so you will be left to study the package body on your own.
USING THE TYPE EXTENSION
Example program ------> e_c22_p5.ada
The example program named Vehicle2 is in the file named e_c22_p5.ada and gives a very limited
example of using the TRANSPORT and the CAR class. There is nothing new here except for the
fact that the direct access of the Wheels component is not used here since it is now hidden from view.
The next example program will finally provide full inheritance and extension of both components and
operations. This is what is usually meant when inheritance is referred to in articles on object oriented
programming.
REAL TYPE EXTENSION
Example program ------> e_c22_p6.ada
The example program named e_c22_p6.ada contains our first example of real inheritance and
extension and is a good example of the way it is actually used in practice. The last few example
programs, even though they are valid Ada programs, do not illustrate real inheritance, but were
intended to show that there are more subtle forms of inheritance built into the Ada language.
We begin by declaring a private TRANSPORT type in line 5, but with an extra reserved word
added, namely tagged. The word tagged indicates to the Ada compiler that this type may be
extended with additional components and/or operations to define a completely new type. The actual
definition is given in lines 35 through 39 and should look very familiar by now except for the
reserved word tagged which is added here also. Three subprograms are declared in lines 7 through 11
in much the same manner that they have been added in previous programs in this chapter. Except for
the addition of the word tagged, this package is identical to the previous example package.
NOW FOR THE REAL DIFFERENCE
When we define the CAR type, things look a lot different than they did in the last program. This time
the parent type is mentioned in the declaration along with a new use of the reserved word with. We
have been using this word to with packages into our various programs but now it is used to indicate
that something new is going to be added to the parent type to generate the new type. Lines 41 through
44 contain the full declaration of the new type CAR. It says to create a new derived type of
TRANSPORT with some additions because of the reserved word with being used here. The
component named Passenger_Count is included in the CAR type along with the two components
from TRANSPORT, giving the CAR type three variables. We have finally found a way to extend
the number of components in an inherited type. Note that you can only add components. It is not
possible to remove components or to override components.
The CAR type also inherits the three subprograms from TRANSPORT, but it provides its own
Set_Values procedure which only sets the value of the Passenger_Count component. Presumably,
objects of the CAR type will be required to use the Set_Values procedure provided by the parent
class in order to set the values of the two inherited components. This is what will actually be done in
the next example program. The CAR type also provides a new subprogram named
Get_Passenger_Count to return the number of passengers. Overall, the CAR type consists of three
components and provides five subprograms which can be used to manipulate objects of this type.
It is possible to extend a type by adding subprograms, and it is possible to modify the functionality of
a type by overriding one or more subprograms, but it is not possible to eliminate subprograms from
the new type which are part of the parent type. Even though the TRANSPORT type is private, the
CAR inherits all of the primitive operations of the TRANSPORT type because the TRANSPORT
type is tagged. This is true inheritance because all of the entities are inherited.
WHAT DOES IT MEAN TO FREEZE A TYPE?
We can add all of the functionality we desire to a type until we use that type to define a variable or
inherit it into a new type. After it is used, we are not allowed to add functionality to the type because
we would have two variants of the type, the one where it was used and the newer changed version.
The two would be incompatible even though they had the same type name. For this reason, the type is
"frozen", which means you cannot add more primitive operations to it.
ANOTHER NEW TYPE
Since the TRUCK type is very similar to the CAR type, little needs to be said except that it adds two
components to those inherited, and it adds two subprograms to those inherited from the parent. The
TRUCK type is a little different because it provides initialization data for all four variables in its
procedure named Set_Values, so it has no need for the procedure of the same name in the parent
type.
The BICYCLE type is declared in line 31 in exactly the same manner as the CAR and the TRUCK.
When we get to line 52 where the private type is actually defined, it looks a little different than the
other two new types. The with null record at the end of the line tells the system that the BICYCLE
type will have all of the components of the parent type and no more. It is necessary to explicitly tell
the system that none will be added.
We now have the ability to add components and subprograms to an inherited type. We can add
subprograms without adding any components. We can also add components without adding any
additional subprograms, but that would not be very useful because we would have no way to use the
new components since they would be hidden in the object.
WHAT IS A CLASS IN ADA?
A class in Ada 95 is a group of types with a common ancestor, and the name of that class is the type
name of the highest ancestor. The present example program contains the class named TRANSPORT
which consists of the types TRANSPORT, CAR, TRUCK, and BICYCLE. It would also be correct
to say that it contains the class named CAR that consists of only the type CAR. Any hierarchy of
types compose a class with the class name being the name of the highest class in the hierarchy. The
class concept is not very important at this time, but it will become important when we study dynamic
dispatching in the next chapter.
THE PACKAGE BODY
The package body, given in lines 58 through 121, is nothing more than the same implementations we
have been using in this chapter. All of the inheritance and extension constructs are in the specification
part of the code.
There is one small detail that the student should take note of in line 104 and 105, where the
components inherited from the parent class are initialized. Nothing new there, but in line 108 the
procedure named Set_Values from the parent class is used to initialize the same two variables. Since
the types are different, it is necessary to perform a type transformation from the TRUCK type to the
TRANSPORT type to get the compiler to accept it. Note that either method of initialization can be
used. The two methods were given here only as an example.
USING THE REAL TYPE EXTENSION
Example program ------> e_c22_p7.ada
Examine the file named e_c22_p7.ada which serves as an illustration of how to use these new types
we have just generated. As expected, the new package is both with'ed and use'ed into this program,
and the four new types are available for use as needed. There is nothing special about the new types,
except for the fact that three are descendants of the fourth. They are used just like any other types
would be used as can be seen by inspecting this example program. You will notice that two procedure
calls are necessary to set all of the values in the CAR type objects, but only one call is necessary to
set all of the values in the TRUCK type objects. This is only because of the way we declared them in
the package. In a real program it would be best to declare them both in a similar manner to make the
code easier to understand.
Be sure to compile and execute this program.
INITIALIZERS AND FINALIZERS - CONTROLLED
Example program ------> e_c22_p8.ada
The example program named e_c22_p8.ada illustrates automatic object initialization and automatic
cleaning up when the object is about to be destroyed. We include the Ada.Finalization package for
use here, and declare a type named WIDGET which inherits from the type named CONTROLLED.
We override three procedures from CONTROLLED with the predefined names Initialize, Adjust,
and Finalize, each with one parameter of the type of record we just declared. The package body
contains the definitions of the three procedures which are trivial because they each output only a
single line of text to the monitor.
In the main program, we include the Component package in the with list, and we write a trivial
program that does nothing but output a few lines of text. When we execute this program, we get a bit
of a surprise because the three procedures in the Component package are executed even though we
never called them, the system did. They are very special because they each have a special job to do.
Initialize - When any object of this type is defined, this procedure is run automatically, without the
programmer calling it explicitly. This is the place to initialize the components of the class or include
any other initialization code that needs to be executed for each object of this type.
Adjust - Following an assignment, you may have some cleaning up of the data within the new copy.
This procedure is called automatically for the new object after the new data has been assigned to it.
Finalize - When you assign something to a variable, the data contained in that variable is overwritten
and lost forever. You may wish to do something special with the data before it is overwritten, and
that is one of the jobs for this procedure. When you are finished with an object and you wish to
destroy it, or it goes out of scope, you will need to do some cleaning up if it has an access variable to
something on the heap that you are finished with, or some other job that must be done prior to
destroying the object. This procedure is called automatically by the system.
You will notice that the Initialize procedure is executed twice prior to the beginning of the program,
because there are two variables. The Finalize and the Adjust procedures are both executed for each
of the two assignment operations as is evident from the listing. The Finalize procedure is called once
for each of the two objects when they go out of scope at the end of the program.
These three procedures can be a great aid when you are using types that are rather complex.
PROGRAMMING EXERCISES
1. Add a BICYCLE object to e_c22_p7.ada and exercise it to prove to yourself that you can use
the inherited objects correctly.(Solution)
2. Add a new function to the BICYCLE class in e_c22_p6.ada to retrieve the weight in ounces.
Name the new function Number_Of_Ounces. One pound is 16 ounces.(Solution)
Advance to Chapter 23
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 23
MORE OBJECT ORIENTED PROGRAMMING
A CLASS WIDE PROCEDURE
Example program ------> e_c23_p1.ada
Examine the example program named e_c23_p1.ada for yet another new construct available in Ada
95, the class wide procedure. By this time you should be very familiar with the record definitions in
the package specification, where we declare 4 records in lines 8, 18, 26, and 38, three of which are
descended from the parent, which is of type TRANSPORT. Ada calls the combination of all four of
these records a class because they all descend from a common parent. In fact, they are all descended
from the type TRANSPORT so the combination of all four are referred to as the TRANSPORT
class.
The procedure named Print_Values is declared in line 43, and it uses a single parameter with a very
odd looking type, namely the type TRANSPORT'Class. This says that an actual parameter of any
type of the TRANSPORT class can be passed into this procedure, which means that any type that
inherits TRANSPORT either directly or indirectly is a candidate for this parameter. The
implementation for the Print_Values procedure is given in lines 136 through 149 where some of the
components of the parameter named Any_Vehicle are displayed on the monitor.
There are several facts that must be considered when studying this procedure. The first one is that it is
not possible to remove any data components when one type is inherited into another type. Elements
can be added, but they cannot be removed. Next, a variable of any of the four types within the
TRANSPORT class can be passed in as the actual parameter, and the only components that will exist
in all four are those in the parent type itself. The CAR type for example, may have additional
components that are not a part of the parent type, so those are not available in all four types.
Therefore, it is not legal to use any components within this procedure unless they are a part of the
parent type, because only those are guaranteed to be a part of all types of the class. Wheels and
Weight are therefore the only variables that can be used in this procedure. A very brief message is
displayed on the monitor containing both of these values, as a trivial example. You will notice that
line 147 is commented out because the component named Passenger_Count is not available in some
of the types, and can not be used in this procedure which is designed to accommodate any variable of
any one of these four types.
The rest of this package is very similar to those studied in the previous chapter of this tutorial. The
diligent student should understand it thoroughly by now.
USING THE CLASS WIDE PROCEDURE
Example program ------> e_c23_p2.ada
The example program named e_c23_p2.ada should be very familiar to you if you just completed a
study of the previous chapter. The only real difference from e_c22_p7.ada is given in lines 36
through 40 where the same procedure is called with objects of three different types. The class wide
procedure named Print_Values is capable of accepting a parameter of any type within the
TRANSPORT class, and all five variables qualify for this.
When the program is executed, you will see that it does print the data values for each of the variables
correctly even though they are of different types. It is therefore legal to pass a variable of any type
into this procedure, provided that the type is a descendant of the TRANSPORT class no matter how
far down the line it is.
DYNAMIC DISPATCHING?
Example program ------> e_c23_p3.ada
The example program named e_c23_p3.ada introduces yet another Ada construct into our repertoire,
the concept of dynamic binding, or polymorphism. In the last program we called one procedure with
several different types, but in this program, we will call several different procedures with one call, by
letting the system figure out which procedure to call for us.
This example is identical to e_c23_p1.ada except for the addition of four new procedures, one for
each type. The specifications for these procedures are given in lines 14, 23, 33, and 39. You will
notice that they are all identical except for the fact that each uses a different type for the single
parameter to be passed in. Notice also that all of the parameter types are in the TRANSPORT class.
The implementation for one of these four procedures is given in lines 85 through 89, and you can
easily find the three other implementations. You will notice that the implementation for each of the
procedures is different because of the difference in the string to be printed. It is important to note that
the code within the body is different in each case, but the interface is identical.
USING THE DYNAMIC DISPATCHING
Example program ------> e_c23_p4.ada
The example program named e_c23_p4.ada will require a good bit of explanation, because there is a
lot of new material here. In lines 8 through 13 we define 6 variables using the four types that are part
of the TRANSPORT class. We also state explicitly that each of the variables is aliased, a reserved
word that we studied when we covered the access type in this tutorial. You will recall that this
permits access type variables to access these variables indirectly.
We define a new access type in line 15 that can access any variable of the right type, provided it is
aliased, including those allocated on the heap, on the stack, or globally. In addition, it accesses the
TRANSPORT'Class which means that it can access any variable of any type within that class,
provided that variable has been defined to be aliased. Finally, to complete our data definitions, we
define an access variable of the new type for use in the program.
The example program is the same as e_c23_p2.ada until we get to line 47 where interesting things
begin to happen. The code in line 47 assigns Any_Pt the address of the Hummer, and line 48 uses
that address to make a call to the procedure named Describe. You will recall that we have four
different procedures named Describe, but only one that has a TRANSPORT type for its parameter.
Since the Hummer is of that type, that is the procedure that will be called here, and the procedure is
dynamically selected to be executed. In line 49 we assign Any_Pt the address of the Limo and use it
as the parameter for the Describe call. This time the procedure associated with the CAR type is
called. This continues with all six variables and the program is complete.
WHAT REALLY HAPPENED?
You will notice that lines 48, 50, 52, 54, 56, and 58 are identical. The only difference in these six
lines of code is the type of the pointer when the call is made, and the type of the pointer is used to
select the procedure to be executed. This selection is done at run-time and is usually called run-time
binding or polymorphism. It seems like we went to a lot of trouble to do this, and we did, but we will
find that this is a very valuable technique for some programming situations.
A little repetition is in order here since this is new material. In lines 40 through 45 we made many
different calls to one procedure, but in lines 48 through 58 we made several repeats of one call, each
of which was dispatched to several different procedures. This is sometimes called dynamic selection
because the selection is made at run time, as opposed to static selection where the selection is made at
compile time. It is often referred to as polymorphism which means many forms of similar entities.
A DYNAMIC BASE CLASS
Example program ------> e_c23_p5.ada
Examine the file named e_c23_p5.ada to see the beginning package for a final examination of
dynamic binding, at least for the present time. You will notice that we declared a simple type named
EMPLOYEE with three components and a single procedure. It is declared to be tagged which will
permit us to inherit this type into other types to build a class of types. This will be used to illustrate
dynamic binding once more. The message printed by the procedure states that it should never be
displayed. We will return to this later and illustrate a method by which the compiler will prevent that
from happening.
TYPE EXTENSION
Example program ------> e_c23_p6.ada
The EMPLOYEE type mentioned above is located in the Person package, and the three types in the
file named e_c23_p6.ada are located in the package named Person.Positions as indicated by line 6.
We are using the hierarchical libraries available with Ada 95 which could be used to prevent any
possibility of name clashes. The package specification for Person.Positions is very straightforward
and you should be able to understand this package on your own. Notice that there are three
procedures named Display, each with a different type for the formal parameter but otherwise the
interfaces are identical.
The body for the Person.Positions package is not so trivial because of the way strings are defined in
Ada. We mentioned earlier in this tutorial that strings could not be assigned to variables unless they
are all of the same length, or the Ada compiler will issue a type incompatibility error. If we were
building an address list, it would not be very expedient to require everybody to have the same number
of letters in their first name and the same number of letters in their last name. The STRING type
available with Ada must be improved upon in order to make it convenient to use strings in a
meaningful Ada program. We will see how to do that shortly, but in the meantime, we will suffer
through this example program with the current STRING type implementation to show you how
difficult they are to work with. Since we wish to use variable length strings, we will define each
string a little longer than they need to be and associate a counter with each string variable to store the
current length of the string.
Line 43 defines a string variable named Title that can store up to 25 characters and line 44 contains a
variable named Title_Length that will store the current length of the string. If we wanted to store the
name "John", we would put those four characters in the first four locations of Title and store a value
of 4 in the Title_Length variable. We must then diligently define a length variable to be associated
with each string used in this program, and that is exactly what we do. In line 75, we use the Length
attribute to determine the number of characters passed in from the calling program, and execute a
loop to copy the characters one by one from the input string to the internal storage string. You will
notice in line 80, that it is trivial to store away the value of the Salary.
When we get to the Display procedure, we are required to copy the characters to the monitor one at a
time as is illustrated in lines 92 through 94. This is not very convenient, and it is somewhat error
prone because it would be very easy to use a wrong count with some particular string. Even though
the procedures are longer than they really should be, they are very simple, so you should have no
trouble understanding what the package body does.
A SIMPLE TEST PROGRAM FOR THE CLASS
Example program ------> e_c23_p7.ada
The file named e_c23_p7.ada is a very simple program that exercises the EMPLOYEE class that we
have just defined, but it really doesn't exercise it very much. It defines a few variables in lines 8
through 12, fills them with data in lines 16 through 20, and displays the data on the monitor in lines
22 through 26. The calls to Display are nothing special, in fact they only illustrate subprogram name
overloading. There is no polymorphism being used here.
You should compile these three files (e_c23_p5.ada, e_c23_p6.ada, and BUSINESS1.ADA) and link
them together, because they are the basis for some additional operations which we will study in the
remainder of this chapter.
USING DYNAMIC DISPATCHING
Example program ------> e_c23_p8.ada
Examine the file named BUSINESS2.ADA for another example of dynamic dispatching. We did
nothing special in the last three example programs to prepare for dynamic dispatching but we will use
the first two files unchanged to illustrate the use of dynamic dispatching. The file named e_c23_p8.
ada begins by declaring all of the variables as being aliased so that an Ada access variable can access
them within the program. We define an access type for the EMPLOYEE class in line 14, and use
that type to define a variable named Employee_Point in line 15. We initialize all of the variables as
before and we are ready to display the data.
We use the same access variable to access each variable in succession and display the data in each
variable. You will notice that the exact same line of code in used in lines 26, 28, 30, 32, and 34, but it
does not call the same actual procedure each time one of those lines are executed. This is, of course,
because of the way dynamic dispatching works. The procedure that matches the type of the current
value of the access variable is the procedure that will get called.
We used the same two files for the type definitions for each of the last two main programs, but one
used dynamic dispatching, and the other did not. This program is meant to illustrate that there is
nothing magic about the elements of the class that will be used for dynamic dispatching, the critical
portions are in the calling program.
DON'T USE THIS PROCEDURE
Example program ------> e_c23_p9.ada
Return to the file named e_c23_p5.ada to discuss a little problem that we completely overlooked
when we discussed it. Line 29 contains a string indicating that we don't expect anyone to call this
procedure and we would construe it as an error if they did. In fact, we don't expect anyone to ever
create an object of this type, because we don't care to work with EMPLOYEE types, only the more
specific types defined in e_c23_p6.ada.
The example program named e_c23_p9.ada contains a new reserved word abstract that is used in
lines 5 and 11 to indicate that this is an abstract record. Since it is abstract, it is not permissible to
create a variable of this type, and attempting to do so will result in a compile error. The word
abstract is also used in line 7 where it states that the procedure is abstract and can never be called.
Since it will never be called, the procedure does not even need to have an implementation. With the
body of the procedure not being needed, the package body is empty, so it is completely eliminated.
There is then no need for the context clauses for the Ada.Text_IO package, so they are removed.
Any type that inherits the EMPLOYEE type must provide an implementation for Display or contain
an abstract definition for it. Including an abstract definition for Display will make the new type an
abstract record also, and we cannot create an object of the new type. However, including an
implementation for Display, will make the new type a normal type which can be used to define one
or more objects. The compiler will prevent you from ignoring the Display subprogram, and will
require that you include it in every child record of EMPLOYEE.
The most surprising part of this modification is that it can still be used with the files named e_c23_p6.
ada and e_c23_p7.ada or e_c23_p8.ada, with no changes to any of them. Since we never tried to
create a variable of the EMPLOYEE type, there is nothing to change in those files. You should
compile, link, and execute both groups of files to prove to yourself that it actually works as stated.
THIS ENDS THE OBJECT ORIENTED PROGRAMMING LESSONS
This completes our study of object oriented programming in this tutorial. There is a lot more to learn
about object oriented programming and how to use it, but it is beyond the scope of this tutorial, and
there is a plethora of information about the topic in other publications. The remainder of this chapter
will be used to illustrate a better method of using strings in Ada. It is included here because these
example programs lend themselves well to illustrating the particular construct we wish to consider.
RETURNING TO THE STRING PROBLEM
Even though it may seem like we have a STRING problem, we really don't, because the STRING
type is working exactly as it was designed to work. The mark of a well designed language is not that
it has the ability to do everything, but that it has the ability to be extended in an efficient manner to
do anything that needs to be done. Ada was designed to be easily and robustly extended, and the area
of strings is a very good example of this.
There are several string packages available with all Ada 95 compilers. They are all well defined in
the ARM, and should be well defined in your compiler documentation. We will look at one of these
packages to illustrate how it can be used to simplify the use of strings in an Ada program. The
package named Ada.Strings.Bounded contains a generic package named
Generic_Bounded_Length which implements a string package that is much more flexible than the
STRING type. This string type stores an internal count of the number of characters that are currently
significant, much like we did in the e_c23_p5.ada and e_c23_p6.ada files. it also provides a plethora
of subprograms to load, extract, concatenate, and many other operations which are commonly needed
when working with strings.
Example program ------> e_c23_p10.ada
The file named e_c23_p10.ada uses the Generic_Bounded_Length package by instantiating a copy
of it in line 10 which provides an upper limit of 25 characters for a string of this type. The package
provides a type named BOUNDED_STRING which defines the strings we wish to use. Each string
can be from 0 to 25 characters long, and the string object will remember how many characters are
currently stored there. The variable named Name is defined as type BOUNDED_STRING in line
18. The full name of the type is My_Strings.BOUNDED_STRING.You will notice that the generic
package is in the Ada.Strings.Bounded library, so that library is mentioned in the context clause in
line 2. Instead of a use clause, the fully qualified name is used in lines 10 and 11. This is a little
different than the way it is done in most of the tutorial, but either method is completely acceptable.
MORE DYNAMIC STRINGS
Example program ------> e_c23_p11.ada
Examine the file named e_c23_p11.ada for the definition of the other three types, and you will notice
that there are no STRING types in the package specification. They are replaced by the new type
BOUNDED_STRING. Lines 71 through 73 of the package body illustrate the clean syntax that is
possible with this new type because it has an assignment operator available that assigns not only the
text to the new variable, but also the current length.
Since we have no way to output a variable of type BOUNDED_STRING to the monitor, we are
forced to use a loop to output a single character at a time, but the loop uses attributes of the single
variable rather than using two separate variables to control the output. This results in the Display
procedures looking a little cleaner than they did in the last example program. The remainder of this
package is simply more of the same that you can study at your leisure.
THE MAIN PROGRAM IS UGLY
Example program ------> e_c23_p12.ada
Because of the need to maintain the correct types, it is necessary to convert the string constants in
lines 16 through 31 into BOUNDED_STRING types. This is accomplished by calling the
To_Bounded_String function for each of the string parameters prior to passing them into the
Init_Data procedures. This looks ugly, and it seems to defeat the purpose of using the bounded string
package. When writing a string intensive program, you will normally have a small input routine of
some kind, and a small output routine, but there may be a massive amount of string processing going
on within the program. A good example would be a spell checker where there is generally only a
single word input, but an entire dictionary which must be searched.
In addition to the bounded string package, where you define a string type with some upper limit to the
number of characters that can be stored within, there is an unbounded string package. The unbounded
string can store any number of characters. It does this by growing longer automatically as you add
more characters to its length. As you add characters, it reallocates a larger block each time it runs out
of space in order for your string to fit. It will be left up to you to study this package if you wish to use
it.
PROGRAMMING EXERCISES
1. Remove the comment from line 147 of e_c23_p1.ada to see what kind of an error the compiler
issues.(Solution)
2. Define a variable of the EMPLOYEE type in the BUSINESS2.ADA file when you are using
the e_c23_p5.ada file as the definition of that type. Call the Display procedure to see that it
can be used as well as the child types.(Solution)
3. Define a variable of the EMPLOYEE type in the BUSINESS2.ADA file when you are using
the e_c23_p9.ada file as the definition of that type. Does the error reported make sense to you?
(Solution)
Advance to Chapter 24
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 24
BINARY INPUT/OUTPUT
Any useful computer program must have a way to get data into it to operate on, and a way to get the
results out to the user. In part 1 of this tutorial, we studied how to get text in and out of the computer,
now we will see how to get binary data in and out of the computer. Most of the binary input and
output will be to or from files because the data is always in a machine readable format, not one that a
human reader can easily comprehend. A block of binary data could be transmitted to a hardware
device that is designed to respond to the data, or a block of data could be input from a hardware
device reporting on some process. A little more will be said about that later.
BINARY DATA OUTPUT
Example program ------> e_c24_p1.ada
Examine the program named e_c24_p1.ada for an example of outputting binary sequential data. Ada
gives you five data input/output library packages that are defined in the Ada 95 Reference Manual
(ARM), and required to be available with every Ada compiler. They are listed by package name as
follows;
G Ada.Text_IO - This is used for text Input/Output and is always in a sequential mode of
operation. Any file can be used for input or output, but not both. (This package has been in use
during most of this tutorial and was specifically described in chapter 14.)
G Ada.Sequential_IO - This is used for binary Input/Output and is always in a sequential mode
of operation. Any file can be used for input or output, but not both.
G Ada.Direct_IO - This is used for binary Input/Output and can be used in either a sequential or
random mode of operation. Any file can be used for input, output, or input and output.
G Ada.Streams.Stream_IO - This is used for either binary or text Input/Output but permits the
use of heterogeneous data whereas the other three require all data to be of the same type. for
any given file. In the case of Ada.Text_IO, the data must all be of the CHARACTER type,
so it must be of a single type also.
G Ada.Text_IO.Text_Streams - This permits a text file to be used for stream Input/Output so it
can be mixed with binary data in the same file.
You should refer to either your compiler documentation or the ARM for the definition of these
packages. All definitions are found in Annex A. Spend a little time studying the procedures and
functions available with each to become familiar with the capability of each package. The knowledge
you have gained studying this tutorial to this point should enable you to understand most of the
specifications of these five packages.
THE PACKAGE NAMED Low_Level_IO
There is another input/output package that may be available with your compiler named Ada.
Low_Level_IO. This is not required by the ARM, but if it exists, it will be used for machine
dependent input/output programming with your particular implementation. If it exists with your
compiler, the description of how to use it will be included with your documentation, and since it will
be completely different with each compiler, no attempt will be made to explain its use here.
BACK TO e_c24_p1.ada
Although this program does very little, there are several steps that must be performed to output to a
binary file. They will be taken in order, so follow along closely. First we must tell the system that we
wish to use the external package Ada.Sequential_IO, which we do using a with clause in line 4.
Next we define a record type to illustrate one kind of data that can be output to the file, and declare a
variable of the record type in line 18.
In order to use the sequential input/output package, we must instantiate a copy of it, because it is a
generic package and cannot be used directly. We do this in line 15, using the data type we wish to
output to the file, then add the use clause in line 16 to make the new package readily available. We
need an internal file name, and we define it in line 19, but with a minor difficulty because we have an
overloaded file type available. We have made Ada.Text_IO available to illustrate the problem, even
though we don't use it in this program, because it contains a type named FILE_TYPE. The package
named Seq_IO that we have instantiated also contains a type named FILE_TYPE, the one we wish
to use. In order to tell the system which one we want, we must use the extended naming notation to
differentiate between the two types. This is done in line 19 of the example program. With these steps,
we are ready to begin the executable part of the program.
In a manner similar to that used for text files which we studied earlier, we create the file in line 23, in
this case using the mode Out_File since we wish to write to the file. As you recall, this also ties the
internal name for the file to the external name we have chosen, NAMEFILE.TXT, which must follow
the conventions for our particular Ada compiler and operating system. If your operating system is
substantially different, you may need to change this name. We are finally ready to actually use the
output file, so we load some nonsense data into the declared record variable in lines 25 and 26, then
enter a loop where we will change the Age field of the record in order to have some varying data to
write to the file. This will make it more useful when we read the data from this file in another
example program.
WRITING BINARY DATA TO THE FILE
We actually write data to the file in line 30, where we use the procedure Write which is part of the
instantiated package we named Seq_IO earlier, but since we have defined Seq_IO in a use clause,
we do not need the qualifier "dotted" to the procedure name. We mention the internal file name to tell
the system which file we wish to write to, and the variable which we desire to output. The variable to
be output must be of the type for which the package was instantiated, resulting in a rule that all binary
records output to any given file must be of the same type. Even though they must be of the same type,
they can be records of a variant record with different variants.
After writing 100 binary records, we close the file in the manner shown in line 33, and the program is
complete.
We covered a lot of territory in the last few paragraphs, but it is useful information we will need in
the next few example programs, and of course we will need it anytime we wish to actually use a
binary file. It should be clear that we could open several files, each storing a different data type, and
write to them in any order provided that we wrote the proper data type to each file.
The resulting file, named NAMEFILE.TXT, will be used to illustrate binary reading in the next two
programs, so it is imperative that you compile and execute this program. After running it, you will
find the file named NAMEFILE.TXT in the default directory of your system, and if you attempt to
look at it with a text editor, it will have some very strange looking characters because it is a binary
file. You will notice however, that much of it will be readable because of the nature of the data
written to it.
READING A BINARY FILE
Example program ------> e_c24_p2.ada
The example program named e_c24_p2.ada illustrates how to read from a binary file in a sequential
mode. Everything in the declaration part of the program is identical to the last program except for
including the Ada.Integer_Text_IO package in lines 2 and 3 for use later.
The only differences in the executable part is the use of the Open procedure from the Seq_IO
package, which uses the In_File mode of file opening. We can now read from the file we wrote in the
last program, but we cannot write to it from this program. We execute a loop 100 times where we
read a record from the binary file each time through the loop. The record definition is identical to the
record used to write the binary file. If the record is different in structure or in data types, you may get
anything upon reading, and the data probably will not appear to have anything to do with the original
data written, because the bytes will be mixed around.
You may wonder what will happen if the file that we requested the system to open is not available
where we expect it to be. In the spirit of Ada, you may guess that an exception will be raised and you
will be correct. If the file is not available for opening, the exception Name_Error will be raised as an
indicator. There are eight exceptions defined in the package IO_Exceptions that are raised for
various kinds of errors.
Assuming that the file did open properly, all 100 elements are read in and those with the Age field
greater than or equal to 82 are displayed to illustrate that the data really did get written. Finally, the
binary file is closed and the program terminated.
WHAT ABOUT PORTABILITY?
Suppose you wrote the binary file with one Ada compiler, and attempted to read it using a different
compiler. It would be indeterminate whether or not it would work, because each implementor is free
to define the internal bit patterns of the various types to fit his particular compiler. Using such simple
fields as those in this illustration would lead to a very good chance of portability, but using more
elaborate records or arrays, would almost certainly cause incompatibility problems.
The solution to this problem is to read a file with the same compiler that was used to write it. Of
course a file written in a text format, using Ada.Text_IO, is portable and can be read with a different
system.
Compile and execute this program and verify that the data really did get output as desired. If you did
not compile and execute the last example program, you did not generate the file named "NAMEFILE.
TXT", and you cannot successfully execute this program.
RANDOM INPUT AND OUTPUT
Example program ------> e_c24_p3.ada
Examine the file named e_c24_p3.ada for an example of random input and output. Random file
access means that we can output data to the file, or read data from the file just as if the file were an
array. The elements of the file do not have to be accessed in order. We will illustrate all of this in this
example program.
The declaration part of this program should look familiar to you since it is nearly identical to the last
two example programs. The biggest difference is the use of the Ada.Direct_IO package instead of
the Ada.Sequential_IO package. We instantiate a copy of this called Ran_IO, and use it to declare
the internal filename, My_In_Out_File. Next, as part of the declaration part of the program, we
declare a function that will be used to output some well formatted data.
READING FROM THE RANDOM FILE
Before we can do anything with the file, we must open it, and since we intend to read from and write
to this file, we open it with the InOut_File mode. Of course we use the internal filename we defined
in line 19, and the external filename we have already written to. In line 36, we use the procedure
Read, reading from the file we have opened, and we read the data into the record variable named
Myself. The thing that is really new here is the use of the number 37 as the third actual parameter of
the procedure call. This tells the system that we wish to read the 37th record from the designated file.
We use our Display_Record procedure to display the record read, then tell the system that we wish
to read record number 25, and display it. In line 40, we don't tell the system which record we want to
read explicitly, so it returns the next record, the 26th, which we display.
WRITING TO THE RANDOM FILE
We fill the three fields of the record variable with nonsense data in lines 44 through 46, and write the
modified record to records 91, 96, and 97. We write to record 97 because we don't specify a record
and the system will default to the next successive record number. In line 51 we call the procedure
Set_Index with the value of 88, and as you may guess, it sets the record pointer to 88, which is the
next record that will be read by default if no record number is stated. The loop in lines 52 through 55
read and display all records from 88 through the last, because we keep reading until we find an end of
file. You will see when you compile and execute this program that we did modify records numbered
91, 96, and 97.
You will find binary Input/Output to be very useful for temporary storage of large amounts of data,
but you must keep in mind that any data you write using this technique may or may not be readable
by some other system. For this reason, binary output should not be used except for data that is meant
to be read by another program compiled with the same Ada compiler as the data generator. Be sure to
compile and execute this program.
USING HETEROGENEOUS DATA
Example program ------> e_c24_p4.ada
Examine the example program e_c24_p4.ada which contains an example of writing several types of
data to a single file using the streams capability which was added to Ada 95. The package named
Ada.Streams.Stream_IO contains the required entities, so it is included in context clauses in lines 3
and 4, and we declare a file object along with an access type for use with the file. Note that even
though the package is included in a use clause, the extended naming notation is required in line 8
because there is also a type FILE_TYPE defined in Ada.Text_IO, and the system does not know
which to select based on the available information. The extended naming notation is not required in
line 9, but is given to indicate clearly that the two types work closely together.
In lines 11 through 23, we define several types and various variables for use within the executable
portion of the program. They are all assigned initial values which have no significance, but provide
known values for all entities.
In line 27, we create a stream with the name My_File which will allow us to write to the file named
"funny.txt" in the default directory. In line 28 we cause the access variable named My_File_Access
to access the file we just opened. In lines 30 through 36, we write seven different variables, which
represent three different types, to the same file in an arbitrary order. The method of writing seems
very strange, and it is, because it uses an attribute of the types to do the actual writing. The file is
closed in line 38, and the same file is opened for reading in line 41.
The first two variables are read from the file in lines 44 and 45, being careful to read them in the
proper order and using the proper type for each variable. To illustrate that the data did get read in
properly, the value of Animal is displayed on the monitor for inspection. The file is closed a second
time in line 52, and the program is complete.
This very limited program illustrates only one use for the streams capability provided with Ada 95.
This package also provides for random input/output rather than only sequential, and the stream can be
directed into memory rather than into a file, or read directly from memory.
PROGRAMMING EXERCISES
1. Modify e_c24_p1.ada to write the same data to two different files, except when Age is
between 50 and 60. During this range, one of the characters should be changed for one of the
files.(Solution)
2. Modify e_c24_p2.ada to read both files output from exercise 1 and list the differences in the
two files.(Solution)
3. Combine e_c24_p1.ada and e_c24_p2.ada in such a way that the file is written in one loop,
then read back in and displayed in a successive loop.(Solution)
Advance to Chapter 25
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 25
DYNAMIC ALLOCATION
Although this chapter is titled "Dynamic Allocation", it may be more proper to title it "Sorting with
Linked Lists", since that is what we will actually do. This chapter contains example programs that
will illustrate how to generate a linked list with dynamically allocated entries. It is meant to illustrate
how to put several programming techniques together in a meaningful manner. It will also instruct you
in dynamic allocation and deallocation techniques.
DYNAMIC DEALLOCATION
One of the most important topics covered in this chapter is that of dynamic deallocation. After
variables are dynamically allocated and used, they can be deallocated, permitting the memory space
to be reclaimed for reuse by other variables. Ada uses two techniques, one being garbage collection,
and the other being unchecked deallocation. We will have a good bit to say about both of these in this
chapter.
A SIMPLE LINKED LIST
Example program ------> e_c25_p1.ada
The program named e_c25_p1.ada contains all of the logic needed to generate and traverse a linked
list. We will go through it in detail to illustrate how to build and use a linked list.
The first thing we need for a linked list is a record type containing an access variable which accesses
itself. Line 9 is an incomplete record definition which will allow us to define the access type in line
11. After we have the access type defined, we can complete the record definition in lines 13 through
17 which includes a reference to the access type. The record type therefore contains a reference to
itself. Line 9 is more properly called a type specification and lines 13 through 17 a type body. Note
that the incomplete type definition can only be used to declare an access type.
We declare two additional access variables in lines 19 and 20, and two procedures to be used to
generate and traverse the list as we will see shortly. Note that all of this is contained within the
declaration part of the main program.
The program itself, beginning in line 56, begins with a for loop covering the range of the string
variable named Data_String, defined earlier, and each character of this string is given in turn to the
procedure Store_Character. It remains for us to discover what this procedure does.
THE PROCEDURE Store_Character
We enter this
procedure with the
single character and
wish to store it
away somehow for
later use. We use a
local variable,
named Temp,
which is an access
variable to our
record type and use
it to dynamically
allocate storage for a variable of the record type CHAR_REC in line 42, then assign the single
character input from the calling program to its field named One_Letter. The field of this record
named Next_Rec is an access type variable, and according to the definition of Ada, the system will
set it to null when it is generated. Figure 25-1 is a graphical representation of the record variable, the
two access type variables defined in lines 19 and 20, named Start and Last, and the local access
variable named Temp. Now we need to insert the new record into our linked list, but there is a
different way to do it depending on whether it is the first record, or an additional record.
THE FIRST RECORD

If it is the first record, or if this is the first time this procedure has been called, the value of the access
variable Start will be null, because the system assigned a value of null to it when it was elaborated.
We can test the value, and if it is null, we assign the value of the new record's access variable to the
access variable Start and to Last. We have generated data that could be graphically depicted by
figure 25-2, and we will see shortly just how this will be used.
AN ADDITIONAL RECORD
If we find that the value of Start is not null, indicating that it has already been assigned to access
another record, we need to add the new record to the end of the list. If it is the second time we have
entered this procedure, we have data as pictured in figure 25-3, which includes the single record
entered earlier, and the new record which is still disassociated with the first but accessed by the
access variable named Temp.

We add the new record to the end of the list in lines 50 and 51, with the resulting list being pictured
graphically in figure 25-4. Line 50 causes the access variable in the first record to access the new
record, and line 51 causes the variable Last to refer to the new record which is now the end of the
list. After entering the third element and adding it to the end of the list, we have the data structure
pictured in figure 25-5. Further elements will not be diagrammed for this example, but it would be
good for the student to draw a few additional diagrams.


TRAVERSING THE LIST
Each time a character is added to the list, the Traverse_List procedure is called which starts at the
input access point, Start in this program, and lists all characters currently in the list. It does this
through use of its own local access variable named Temp which is initially assigned the address of
the first record in the list. It checks first to see if the list is empty, and if it is not, it enters a loop
where it outputs the character in each record until it finds a record with a value of null in its access
variable field named Next_Rec, which is an access pointer. The variable Temp is used to work its
way through the list by the assignment in line 32 where Temp is assigned the access variable's value
from the next record each time through the loop.
Since the list is traversed each time a character is entered into the list, the list of characters output will
grow by one character each time a character is added and the updated list is traversed.
We traverse the list one more time in line 65, then we free the entire list one element at a time. You
will notice that we do not actually deallocate the free'ed elements, we only assign the access variables
the value of null. We will depend on the garbage collector to do the deallocation for us.
MORE ABOUT GARBAGE COLLECTION
We mentioned garbage collection in chapter 13 of this tutorial, but there is more to be discussed
about this subject. An Ada compiler may include a garbage collection facility which will search
through the access variables and the storage pool occasionally to see if there are any locations in the
storage pool that are not accessed by access variables. If it finds any, it will return those locations to
the free list so they will be available for allocation again. In this way, any memory that gets lost,
either through faulty programming, or due to intentionally clearing an access variable, will be
automatically restored and available for reuse as dynamic variables. Note however, that
implementation of a garbage collector is optional in an Ada compiler. Check your documentation to
see if a garbage collector is actually available with your compiler.
USING THE GARBAGE COLLECTOR
In lines 68 through 73, we execute a loop that will traverse the linked list, and assign all of the access
variables the value of null. If there is a garbage collector, it will eventually find the locations in the
storage pool that no longer have an access variable accessing them and return those locations to the
available pool of usable memory. We used the word eventually because there is no predefined time
when this will occur, but is at the discretion of the compiler writer. More will be said about this topic
later in this chapter.
If your compiler does not have a garbage collector, the operating system will reclaim the lost memory
when you complete execution of the program, so no memory will actually be lost.
WHAT IF THE DYNAMIC ALLOCATION FAILS?
As mentioned earlier in this tutorial, if there is not enough memory to provide the requested block of
memory, the exception Storage_Error is raised. It is then up to you to handle the exception, and
provide a graceful way to deal with this problem. You may want to suggest a means of recovery to
the user.
Compile and Execute this program, observe the output, and then return to additional study if you do
not completely understand its operation. You will need a good grasp of this program in order to
understand the next program, so when you are ready, we will go on to a linked list that is a bit more
complicated because it can be used to sort an array of characters alphabetically.
A LINKED LIST THAT SORTS ALPHABETICALLY
Example program ------> e_c25_p2.ada
The next example program, named e_c25_p2.ada, uses a different storage technique that results in an
alphabetically sorted list. This program is identical to the last except for the Store_Character
procedure.
In this program, the Store_Character procedure works just like the last one if it is the first character
input as you can see by inspecting lines 74 through 77. If it is an additional input however, we make a
call to the embedded procedure named Locate_And_Store. This procedure searches through the
existing list looking for the first character in the list that is not less than the new character
alphabetically. When it is found, the search is terminated and the new character must be inserted into
the list prior to the located character. This is done by moving access variables around rather than by
moving actual variables around. If the new character must be added to the starting point of the list, it
must be handled in a special way, and if it must be the last element, it also requires special handling.
ADDING ELEMENTS TO THE LIST
Figure 25-6 illustrates the condition of the data when the fourth element is to be added to a three
element list.

The three element list is identical to the list described in the last example program and Temp is
accessing the new element to be inserted alphabetically. Lines 64 and 65 of the program alter the
access variables to do the insertion, and figure 25-7 illustrates the result of changing the access
variables to achieve that goal. Note that the character data used here is not the same as the data used
in the program, but is different for illustrative purposes. The cases where the new record is added to
the beginning of the list, and when it is added to the end of the list will not be illustrated graphically,
but is left for the student to diagram.

MORE ABOUT Unchecked_Deallocation
In chapter 13, we first mentioned the generic procedure supplied with your compiler named
Unchecked_Deallocation and illustrated how to use it in example programs there. Since it can be
used with any dynamically allocated data, it can be used with these programs also. In order to use it,
you must first mention the name in a with clause as is done in line 2 of this program to make it
available. Because it is a generic procedure, it must be instantiated before use. Line 22 is the
instantiation of the procedure where it is named Free. Pascal and C each have a deallocator available
named Free, and the name Free has become fairly standard in Ada because of the other languages.
You would be encouraged to use the name Free also for ease of communication with other Ada
programmers. It would be perfectly legal to name it any other name provided it obeyed the rules of
naming an identifier.
HOW DO YOU USE Unchecked_Deallocation
When you use the Unchecked_Deallocation procedure, you are essentially taking on the
responsibility for managing the storage pool yourself, and you must tell the system that you will be
responsible for garbage collection, and that it need not concern itself with it. You do this by using the
pragma named CONTROLLED as illustrated in line 25. This tells the system that you will be
responsible for managing all areas of the storage pool that are dynamically allocated to any access
variable of type CHAR_REC_POINT. The system will not attempt to do any garbage collection for
this type, but will assume that you are handling the memory management.
You may think that it would be a good idea to let the system maintain the storage pool and do the
garbage collection automatically but this can be a real problem, which will be evident after we
understand what garbage collection is and how it is implemented.
HOW IS GARBAGE COLLECTION IMPLEMENTED?
There is no predefined method of how often garbage collection should be implemented, so it is up to
each compiler writer to define it his own way. One of the most common methods is to wait until the
storage pool is used up, then do a search of all access variables and all storage pool areas to find all
areas that are unreferenced. Those areas are then returned to the free list and program execution is
resumed. The biggest problem with this is that it may take as much as a full second of execution time
to accomplish this during which time the Ada program is essentially stopped. This is not acceptable
in a real-time system because it could occur at a very inopportune time, such as during final approach
of a 747 into an international airport. In that case, you would do well to use the pragma named
CONTROLLED to tell the system to ignore garbage collection, and manage the storage pool
yourself as we are doing in this program.
DEALLOCATING THE LIST
The loop in lines 95 through 100 will traverse the list and return all of the allocated data to the
storage pool where it will be immediately available for reuse. The observant student will notice that
the access variable from each record is copied to the variable named Last prior to deallocating that
particular record.
Compile and execute this program, so you can see that it really does sort the input characters
alphabetically. It should be apparent to you that this very simple case of sorting single characters is
not really too useful in the real world, but sorting a large list of records containing 23 fields, by last
name, first name, zipcode, and place of birth, could be a rather large undertaking, but could also lead
to a very useful program. Remember that in this program we are only changing access variables
rather than moving the data around, so the efficiency of this technique when using it for a large data
base will be very good.
A BINARY TREE SORTING PROGRAM
Example program ------> e_c25_p3.ada
The example file named e_c25_p3.ada illustrates the use of dynamic allocation and recursion to
perform a sorting function. It uses a binary tree method of alphabetization, which is thoroughly
defined in Wirth's book, "Algorithms + data structures = Programs". The method will be briefly
defined here.
The sorting element is pictured in figure 25-8, where a node is
composed of the stored data within the circle and the two
pointers, each of which point to other nodes or to null values.
Each of these nodes must have something pointing to it to make
the entire system useful, and we need a few additional pointers
to find our directions through the final overall structure.
The definition of the node is contained in the program in lines
14 through 19, where we define the type of the node with 2 access variables pointing to it's own type.
You will see that we have one access variable named Left and another named Right which
correspond to the two legs out of the bottom of the node in figure 25-8. The node itself contains the
data which could be any number of different variables including arrays and records, but for our
purposes of illustration, will contain only a single character.
BUILDING THE TREE
The main program begins in line 74 with a loop to call the procedure Store_Character once with
each character in the input string named Data_String. We traverse the list one more time in line 82,
and assign the root the value of null. The following description of operation will use Test_String as
the string example. The first time we call Store_Character, with the character "D", we allocate a
new record, store the "D" in it, and since Root is equal to null, we execute line 65 to assign the
access variable named Root to point to the new record, resulting in the state found in figure 25-9.
The next time we call
Store_Character, this time with
the character "B", we allocate a
new record, store the "B" in it,
and since Root is no longer
equal to null, we call the
procedure Locate_And_Store
from line 67, telling it to start at
Root. The procedure
Locate_And_Store is recursive,
calling itself successively until it
successfully stores the character. The first time it is called, the input character is less than the stored
character at the input node, which is "D", so the left branch is chosen, and the statement in lines 45
through 49 is executed. In this particular case, the left branch is null, so it is assigned the address of
the input record resulting in the state found in figure 25-10. The tree is beginning to take shape.

THE THIRD CHARACTER
The next character is sent to Store_Character, this time a "C", resulting in another call to
Locate_And_Store. On this pass through the logic, because the input character is less than the
character at the root node, we select the left branch and call Locate_And_Store recursively from line
48. On this recursive call, we tell it to work with the node stored at the left branch of the present
node. In the next procedure call, we find that "C" is not less than the "B" stored there and we find that
the right branch of this node is null, so we can store the new node there. Our tree now looks like that
given in figure 25-11.

Continuing through the remaining characters of our input stream, we finally have the structure as
pictured in figure 25-12 with all 6 characters stored in it.

TRAVERSING THE B-TREE
Traversing the tree is essentially the same as building it, except that we do not need to test for
equality of the input data, only test each node's Left and Right access values. If the access values are
not equal to null, there is a subtree where the access variable is pointing, and we recurse to that
subtree and check each of its subtrees. With a bit of study, you should be able to trace your way
through the tree to see that we actually do alphabetize the input characters by the way we built the
tree, then traverse it for output.
DEALLOCATING THE TREE
Once again we use Unchecked_Deallocation and the pragma CONTROLLED to explicitly
deallocate the tree. We do this by traversing the tree in a manner similar to when we printed the
characters out. One important thing to keep in mind however is that you must free a node only after
you have checked both subtrees, because once you free a node, its subtrees are no longer available.
PROGRAMMING EXERCISES
1. Use Unchecked_Deallocation to deallocate the list in the example program e_c25_p1.ada.
(Solution)
2. Add a variable of type INTEGER named Character_Count to the record named
B_TREE_NODE in e_c25_p3.ada. Store the current character count in this variable as the
tree is generated. When the string is completed, output the sorted character list along with the
position in the string it occupies.(Solution)
B is character 2 in the string.
C is character 3 in the string.
D is character 1 in the string.
F is character 6 in the string.
G is character 4 in the string.
I is character 5 in the string.
Advance to Chapter 26
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 26
SIMPLE TASKING
WHAT IS TASKING?
The topic of tasking is probably new to you regardless of what programming experience you have,
because tasking is a relatively new technique which is not available with most programming
languages. If you need some kind of a parallel operation with most other languages, you are required
to use some rather tricky techniques or write a driver in assembly language. With Ada, however,
tasking is designed into the language and is very easy to use.
BUT NOT TRULY PARALLEL IN ADA
Tasking is the ability of the computer to appear to be doing two or more things at once even though it
only has one processor. True parallel operation occurs when there actually are multiple processors
available in the hardware, but since most modern computers have only one processor, we must make
the system appear to have more than one by sharing the processor between two or more tasks. We
will have much more to say about this later, but we must discuss another topic first.
REAL-TIME REQUIRES TIMING
Example program ------> e_c26_p1.ada
In the initial design of the Ada programming language, a requirement was included that it be capable
of operating in a real-time environment. This requires that we have some control of time. We at least
need the ability to read the current time and know when we arrive at some specified time. The
example program named e_c26_p1.ada will illustrate how we can do just that.
The program begins in our usual way except for the addition of the new package listed in line 5, the
Ada.Calendar package which must be supplied with your compiler if you have a validated Ada
compiler. The specification package for Ada.Calendar is listed in section 9.6 of the Ada 95
reference Manual (ARM) and is probably listed somewhere in your compiler documentation. This
package gives you the ability to read the system time and date, and allows you to set up a timed
delay. Refer to a listing of the specification package of Ada.Calendar and follow along in the
discussion in the next paragraph.
THE CLOCK FUNCTION
You will notice that the type TIME is private, so you cannot see how it is implemented, but you
won't need to see it. A call to the function Clock returns the current time and date to a variable of
type TIME, and other functions are provided to get the individual elements of the date or the number
of seconds since midnight. You cannot read the individual elements directly, because some may
change between subsequent reads leading to erroneous data. A procedure named Split is provided to
split up the type TIME variable and return all four fields at once, and another is provided named
Time_Of which will combine the individual elements into a TIME type variable when it is given the
four elements as inputs.
Finally, you are provided with several overloadings of the addition, subtraction, and some compare
operators in order to effectively use the Ada.Calendar package. A single exception is declared which
will be raised if you attempt to use one of these subprograms wrong.
THE delay STATEMENT
The reserved word delay is used to indicate to the computer that you wish to include a delay at some
point in the program. The delay is given in seconds as illustrated in line 19 of the program under
study, and is declared as a fixed point number, which is defined by each implementation. The value
of the delay is of type DAY_DURATION, but in this case, the universal_real type is used. The exact
definition of the delay is given in Annex M of your compiler. It must allow a range of at least 0.0 to
86,400.0, which is the number of seconds in a day, and it must allow a delta of not more than 20
milliseconds. Refer to Annex M of your compiler documentation to see the exact declaration for this
type for your particular compiler. The ARM requires that when the delay statement is encountered,
the system must delay at the point of occurrence for at least the period specified in the delay
statement, but does not say how much longer the system can delay. This leads to some inaccuracy in
the delay which will be up to you to take care of. We will see how later in this example program.
A fixed point variable is used for the delay variable so that addition of times can be done with no loss
in accuracy, and fixed point numbers have a fixed accuracy.
When you execute this program, you will see the first line displayed on the monitor, then a pause
before the second message is displayed due to the delay statement in line 19. In fact, the pause will be
at least 3.14 seconds according to the Ada specification.
USING THE CLOCK FUNCTION
In line 22, the Clock function is used to return the current time and date and assign it to the variable
named Time_And_Date. In the next line we use the procedure named Split to split the time and date,
which is contained in the composite variable named Time_And_Date, into its various components.
Although the only component we are interested in is the Seconds field, we must provide a variable
for each of the other fields simply because of the nature of the procedure call. Within the function
call, we assign the value of Seconds to Start for later use. This is a record of the time when we
started the loop which we will use later. The time is in the form of the number of seconds that have
elapsed since midnight according to the definition of the calendar package.
We execute a loop in lines 25 through 38 where we read the time and date, split it into its component
parts, and display each of the components. Instead of displaying the time since midnight, we subtract
the Start time from the current time in line 35, where we are actually using one of the overloadings
from the Ada.Calendar package. We display the elapsed time since we executed line 22 of this
program. Finally, we put in a total delay of one second each time we pass through the loop so we can
see the delays accumulate.
WE ARE ACCUMULATING ERRORS IN THIS LOOP
You will recall that the delay statement requires a delay of at least the amount listed, but says nothing
of extra delay allowed when returning to the program, in order to give the compiler writers leeway in
how to implement the delay statement. In addition, we will require some time to execute the other
statements in the loop, so it should not be surprising to you that when you compile and execute this
program, the time will not advance by exactly one second for each pass through the loop, but will
precess slightly as time passes.
A LOOP WITHOUT ERRORS
In lines 42 through 51, we essentially repeat the loop but with a slight difference. Instead of delaying
for one second in each loop, we delay the amount needed to get to the desired point in time. In line
50, we convert the type of Index to DAY_DURATION with an explicit type conversion, then
subtract the current elapsed time to calculate the desired time of the delay. This prevents an
accumulation of error, and when you run the program you will see only the digitizing error
introduced by the fixed point number. However, there is a potential problem with this method.
WHAT IF A NEGATIVE DELAY IS REQUESTED?
When calculating delay times like this, it is possible for the required delay time to result in a negative
number. The Ada designers had enough foresight to see that in most applications, you would desire to
simply push forward, so they defined the delay such that a negative value for the delay time would be
construed as a zero, and no error would be raised. If a negative time should be considered an error
condition for your application, it is up to you to detect it and issue an appropriate error message, or
raise an exception.
The final point to be made about this example program is the delay until statement illustrated in line
56. The system delays here until the absolute time given which is of type Time. If the time given is
previous to the time when this statement is executed, there will be no delay. The rest of this loop is
trivial, so you can study it on your own.
Be sure to compile and execute this program and observe the output. Due to the delays in the first
loop, the data output to the monitor is somewhat irregular and can be seen when the program is
executed.
THE delay IS NOT PART OF CALENDAR
One final point must be made before we leave this program. The Ada.Calendar package and the
delay statement were both introduced here, and even though they work well together, they are
completely separate. The delay statement is not a part of the Ada.Calendar package as will be
evidenced in the example programs later in this chapter.
OUR FIRST TASKING EXAMPLE
Example program ------> e_c26_p2.ada
Examine the file named e_c26_p2.ada for an example program containing some tasking. The first
thing you should examine is the main program consisting of a single executable statement in line 38
that outputs a line of text to the monitor. It may seem strange that it doesn't call any of the code in the
declaration part, but it doesn't have to as we shall see.
An Ada task is composed of a task specification and a task body, the former being illustrated in line
7, and the latter being illustrated in lines 8 through 15. This is the first task and both parts begin with
the reserved word task. The structure of a task is very similar to the structure of a subprogram or
package. This first example is a very simple task which executes a for loop containing output
statements. The end result consists of four lines of text being displayed on the monitor.
THE TASK SPECIFICATION
The task specification can be much more involved than this one, but this is a good first example. The
general structure for the task specification is given by;
task <task-name> is
<entry-points>;
end <task-name>;
but if there are no entry points, then only the reserved word task is needed followed by the task
name. As with the package, the task specification must come before the task body.
THE TASK BODY
The task body begins with the reserved words task and body, followed by the task name, and the
remainder of the structure is identical to that of a procedure. There is a declarative part where types,
variables, subprograms, packages, and even other tasks can be declared, followed by the executable
part which follows the same rules as those for a main program. In this case, there is no declarative
part, only an executable part. When this task is executed, it will output four lines to the monitor and
stop. We will say a little more about this task when it gets executed in a few minutes.
TWO MORE TASKS
It should be clear to you that there are two additional tasks in this program, one in lines 17 through 25
and another in lines 27 through 35, each with its own respective task specification and task body.
There are actually four tasks, because the main program is itself another task that will run in parallel
with the three explicitly declared here. Since it is very critical that you understand the order of
execution of the various tasks, we will spend some time defining it in detail.
Ada always uses linear declaration and when it loads any program, it loads things in the order given
in the listing. Therefore when it finds the task body beginning in line 8, it elaborates all of its
declarations, although there are none in this case, then loads the executable part of the task, but does
not begin execution yet. It effectively makes the task wait at the begin in line 9 until the other tasks
are ready to begin execution. It does the same for the task named Second_Task, and also for
Third_Task. When all declarations are elaborated it arrives at the begin of the main program in line
37. At this point, all four tasks are waiting at their respective begin statements, and all four tasks are
permitted to begin execution at the same time. All are of equal priority, but a strange thing happens
when they begin execution.
ORDER OF EXECUTION IS UNDEFINED BY ADA
The rules of execution, as defined by the ARM, give no requirements that any form of time slicing be
done, nor is it illegal for an implementation to allow starving to occur. Starving is where one task
uses all of the available time and the other task or tasks are allowed to starve because they receive no
time for operation. In addition, there is no required order of execution concerning which of the four
tasks in our example will execute first, because we have not included any form of priority. Because of
these rules, we cannot predict exactly what your implementation will do, but can only give the results
of executing this program on one particular validated Ada compiler. As indicated by the results of
execution listed following the program, this particular compiler allows the task named Third_Task
to utilize all computing power until it runs to completion, then Second_Task uses all of the
resources, followed by First_Task, and finally the main program runs. Your particular compiler may
execute these statements in a different order, but that is still correct. The only requirement is that all
17 lines be output in any order. Of course the order of output is defined within any task according to
the rules of sequential operation. For example, pass number 2 of Third_Task must follow pass 1 of
the same task.
HOW DOES TASKING END?
When the task named Third_Task arrived at its end statement in line 35, it had executed all of its
required statements and had nothing else to do, so it simply waited there until the other tasks were
completed. When all four tasks, including the main program task, had arrived at their respective end
statements, the Ada system knew there was nothing else to do, so it returned to the operating system
as it does at any normal program completion.
Because of the way this program operates, it is not clear that all four tasks are operating in parallel,
but they actually are, as we will see in the next example program. Compile and execute this program
and study the output from your compiler to see if it is different from that obtained as output from our
compiler.
ADDING DELAYS ADDS TO THE CLARITY OF OPERATION
Example program ------> e_c26_p3.ada
Examine the next example program named e_c26_p3.ada and you will notice that delay statements
are added within each of the loops. The delays were chosen to illustrate that each loop is actually
operating in parallel. The main program is identical to the previous program if you ignore the
commented out statements, and since the main program has no delay prior to its output statement, it
will be the first to output to the monitor. The main program will also be the first to complete its
operation and will then patiently wait at its end statement until the other three tasks complete their
jobs.
Because of the differences in the delays, the three tasks will each complete their delays at different
times and the output should be very predictable. If you examine the result of execution given at the
end of the program, you will see that the three tasks actually are running in parallel as we stated
earlier. It would be profitable for you to compile and execute this program so you can observe the
output firsthand.
WHAT ABOUT THE MAIN PROGRAM?
Return once again to the program named e_c26_p3.ada so we can examine the main program. You
should remove the comment marks from the three statements in the main program so that it also
contains a loop and a delay within each pass through the loop. When you compile and execute the
program as modified, it will execute a delay prior to the first output, and will output a total of five
lines to the monitor interlaced with the outputs of the other tasks. This should be a good indication to
you that the main program is acting just like another task. Compile and execute this program, as
modified, to see that the main program acts just like another task.
A BLOCK CAN HAVE TASKS WITHIN IT
Example program ------> e_c26_p4.ada
Examine the program named e_c26_p4.ada for an example of a main program with tasks embedded
within it. There is a block declared in the main program in lines 11 through 44 which is executed
inline just like any other block. This block however, has the same three tasks we have used in the
previous programs embedded within it. You will notice that the order of declaration is different here,
since the three task specifications are given together in lines 12 through 14, but this is perfectly legal
and would be legal in the last two programs also. The only requirement is that the task specification
must be given before the task body for each declared task.
WHAT GOOD IS A DELAY OF ZERO?
You will notice that all of the delays are of zero time, which may lead you to ask why we should even
bother to include the delays. Each time the system encounters a delay of any kind, it must look at
each task to see if any of them have timed out and need to be exercised. When it does that, it may
advance to the next task in order, but it is not required to. Which task will be selected as the next task
is undefined by the ARM, so any task can be executed next, including the one that is presently
executing. The end result is that all four tasks, including the one in the executable part of the block,
may be executed in a round robin fashion, and none of the tasks are starved. It would be perfectly
legal for a single task to starve the others, according to the ARM, so if your compiler allows this, it is
not an error. Tasking, as used in a real programming situation will not have this problem since
additional constructs will be included as we will discuss in the next few chapters of this tutorial.
In a manner similar to that in the other example programs, when all four tasks are at their end points,
the block is completed, and the remaining statement is executed in the main program. Note carefully
that the main program did not enter into the tasking that was executed within the block. The inline
block was executed as another sequential statement as part of the main program. Thus line 9 was
executed, then the block in lines 11 through 44 was executed. When all four tasks including the one
within the block body were completed, the output statement in line 46 was allowed to execute.
Compile and execute this program and study the results. Use the results to prove to yourself that the
statements in lines 9 and 46 of the main program did not enter into the tasking.
PROGRAMMING EXERCISES
1. Write a procedure that can be called from anywhere in the program named e_c26_p1.ada that
will use the Seconds field returned from the procedure named Split, and display the time of
day in hours, minutes, and seconds. Call this procedure from a few places within the program.
(Solution)
2. Modify the program named e_c26_p4.ada to include the executable statements in lines 9 and
46 within loops, with delays, to prove that the statements are executed in a sequential fashion
and do not enter into the tasking which is restricted to the block in lines 11 through 44.
(Solution)
Advance to Chapter 27
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 27
THE SIMPLE RENDEZVOUS
COMMUNICATION BETWEEN TASKS
In the last chapter, we ran a few programs with tasking, but they all used simple tasking with no form
of synchronization. In a real situation using tasking, some form of communication between tasks will
be required so we will study the simple rendezvous in this chapter.
Example program ------> e_c27_p1.ada
Examine the program named e_c27_p1.ada which will illustrate the simple rendezvous. This program
consists of two tasks, the one in lines 7 through 22 and the main program itself.
THE entry STATEMENT
The task specification is a little more complicated here than it was in any of the example programs in
the previous chapter because we have an entry point declared in the task. The reserved word entry
begins the entry declaration and is followed by the entry name, which is Make_A_Hot_Dog in this
case. The entry statement defines the interface to the outside of a task in much the same way as the
procedure header defines the external interface to a package in the package specification. Unlike a
package, no types, variables, or constants are allowed to be declared in the task specification, but
there is no limit to the number of entry points allowed. An entry can have formal parameters declared
as part of the entry name, (we will have an example in the next example program), but it cannot have
a return parameter similar to a function. One or more of the formal parameters is permitted to have a
parameter of mode out or in out however, so data can be passed both ways while a synchronization is
effected. The in mode is also permitted. We will have more to say about this topic later in this
chapter.
THE accept STATEMENT
The task body is a very simple sequence of statements in which a message is output to the monitor, a
loop is executed four times, and another message is output. The thing that is new here is the accept
statement within the loop. The accept statement begins with the reserved word accept and has the
following general form;
accept <entry-name> do
<executable statements>
end <entry-name>;
and any legal Ada statements can be contained within it. When execution of the task reaches the
accept statement, the task "goes to sleep" until some other task makes a call to this particular accept
statement. The term "goes to sleep" means that the task does not simply sit there and execute a do
nothing loop while it is waiting, effectively wasting the resources of the system. Instead, it is actually
doing nothing until it is awakened by an entry call. It is also possible to have an accept statement
with no statements contained within it. It will have the following form;
accept <entry-name>;
This statement will only be used for task synchronization since there is no data passed to the entered
task.
THE ENTRY CALL
The main part of the program is another loop with four iterations followed by a statement to display a
line of text on the monitor. The only thing unusual about the loop is the statement in line 26 which is
an entry call to the entry named Make_A_Hot_Dog in the task named Gourmet. Keep in mind that
these are two tasks that are operating in parallel and we will carefully explain what is happening.
The entry call is executed at line 26 which wakes up the sleeping task at line 15, and the task named
Gourmet continues from where it went to sleep. Notice that the calling program is not controlling the
execution of Gourmet, but Gourmet itself is in control now that it has been allowed to continue
operation. The entry call is not like a procedure call where the sequence of operation continues in the
procedure, but is instead only a synchronization call that allows Gourmet to continue what it was
doing prior to being put to sleep.
WHAT IS THE CALLING PROGRAM DOING NOW?
During the time that the called task is executing statements within its accept block, the calling task is
effectively put to sleep, and must wait until the called task completes its accept block. When
Gourmet reaches line 19, both tasks are allowed to operate in parallel again until one or both reach
their point of rendezvous again. If the main program reaches its entry call before Gourmet is ready to
accept the call, then the main program will wait until Gourmet is ready. The accept statement, and
the corresponding entry call, are therefore used to synchronize the two tasks.
If you were to move the end of Make_A_Hot_Dog to the line immediately after the accept
statement, including a null of course, the output statements would then be running in parallel. The
delays have been selected in such a way that after making this change, the hot dog would be eaten
before it was made. This is one of the programming exercises at the end of this chapter.
A COUPLE OF FINE POINTS MUST BE MADE HERE
Although the entry call looks very much like a procedure call, it is different because it is not legal to
use a use clause for a task. It is required therefore that every entry call must use the extended naming
convention, or the "dotted" notation. Renaming can be used to reduce the size of the names used and
will be illustrated in the next program.
You will notice that the two tasks were composed of exactly four calls and four executions of the
accept statement. This was done on purpose in order to simplify the problem of task termination at
this point in our study. The study of task termination will be covered in detail in the next chapter.
Until then, you should see what happens when the two loops are different. Change the loop in the
Gourmet task to 5 iterations and see what happens if it waits to accept a call that never comes, then
make the loop in the main program bigger to see what happens when there is nothing to accept its
call. We will study tasking exceptions in a later chapter of this tutorial.
The execution of a return statement (not illustrated here), within an accept statement corresponds to
reaching the final end and effectively terminates the rendezvous.
SERVERS AND USERS
Tasks are categorized into two rather loosely defined groups, server tasks and user tasks. A server
task is one that accepts calls from other tasks, and a user task is one that makes calls to one or more
server tasks. In the present example program, Gourmet is a server task, and the main program is a
user task. Some tasks both accept calls and make calls, and they are referred to as intermediate tasks.
This terminology, or some other fairly self defining terminology may be used in the literature about
Ada, so you should be aware that such classifications may exist.
Be sure to compile and execute this program making the changes suggested earlier and observe the
results.
MULTIPLE ENTRY POINTS
Example program ------> e_c27_p2.ada
Examine the program named e_c27_p2.ada for a few added tasking topics beginning with multiple
accept statements. You will notice that there are three accept statements in the task body
corresponding to a single entry point declared in the task specification. There is no limit to the
number of accept statements and they are executed when the logic causes execution to arrive at each
one in turn. Remember that the task is executed in the logical order as defined in the sequence of
statements, except that each time the logic causes execution to arrive at an accept statement, the task
will "go to sleep" until an entry call is made to the waiting accept statement. Moreover, the logic
does not care where the entry call comes from, nor does it know where it came from, it only cares
that the entry call is made. Thus it is possible for several different tasks to be calling this entry point
and allowing the task to progress through its logic. Because there is only one other task in this
program, we know exactly where the entry call is being generated.
Careful study of the logic will reveal that the accept statement in line 18 must be called, followed by
four calls to line 26, and another call to line 36. After six calls to this entry point, the task will reach
the end of its execution and will be completed. You will notice that we make exactly six calls to this
entry point by the task which is the main program, so everything will come out right.
RENAMING A TASK ENTRY
The alternate name, which is declared in line 11, is used in the main program task to illustrate the use
of the renaming facility. Note that the dotted notation is not required in this case. Once again, you can
change either the number of calls or the number of accepts to see the exception error or a deadlock
condition.
ENTRY PARAMETERS
You should have noticed by now that each of the accept statements has a formal parameter associated
with it in the same manner that a subprogram can have. In fact, it is permissible to have as many
parameters as you desire to transfer data either from the calling task to the called task or back the
other way. All three modes of parameter passing are legal for use and the in mode will be used if
none is specified as is done here. There is one difference in a task however, you are not permitted to
have a return parameter like a function has, but you can return a value by using an out or an in out
parameter.
Since you can pass parameters both ways, the task appears to be executed just like a procedure, but
there is a very definite difference as mentioned earlier. A procedure is actually executed by the
calling program, but the task is simply let free to run on its own in parallel with the calling program.
The task is not a subservient program but is running its own logic as it is coded to do.
Be sure to compile and execute this program after you understand what it is supposed to do.
MULTIPLE CALLING TASKS
Example program ------> e_c27_p3.ada
Examine the program named e_c27_p3.ada for an example of two tasks calling a third task's entry
point. The task named Gourmet has a single entry point, but this time there are two formal
parameters declared in the task specification and the same two declared in the accept statement.
These must agree of course. The entry point named Make_A_Hot_Dog in line 16 contains some text
to display and a delay of 0.8 second, because it takes a little time to make a hot dog. You will notice
that sometimes mustard is added, and sometimes it is not, depending on who is eating the result. We
will say more about this later.
We have two additional tasks in lines 35 through 53 named Bill and John, each of which executes in
parallel, and each of which requests a number of hot dogs and consumes them in zero time, since
there is no delay prior to their next request. You will also notice that once again the numbers come
out right because Bill requests three hot dogs and John asks for two while the task that supplies them
makes exactly five. We will discuss better methods of task termination later. For the time being,
simply accept the utopian situation depicted here.
ENTRIES STACK UP AT THE ENTRY POINT
Considering all that we have said so far about tasking, you should understand that all three of these
tasks begin execution at the same time and Bill and John both request a hot dog immediately. Since
there is only one entry point, only one can be served at a time, and the Ada definition does not
specify which will be serviced first. It does specify that both will ultimately be served, so there is an
implicit queue at the entry point where requests can be stored until they can be serviced in turn. All
requests are serviced on a First In First Out (FIFO) basis with no concern for priority of tasks (to be
defined later). This queue is a "hidden" queue as far as you, the programmer, are concerned because
you have no access to it. You cannot therefore, sort through the entries and redefine the order of
execution of the various entry requests. The attribute named COUNT is available which will return
the number of requests pending on any entry queue. Its use will be illustrated in the program named
e_c29_p6.ada in chapter 29 of this tutorial.
After we study some of the advanced tasking topics, you will have the ability to define several queues
with different priorities and set up your own priorities as needed.
THE ORDER OF OUTPUT IS SORT OF ODD
Back to the program at hand. Examining the result of execution will reveal that for some unknown
reason, the task named John made the first request and the task named Gourmet made a hot dog
with mustard first, because John's task was the one requesting a hot dog with mustard. However,
before John was allowed to continue execution, the Gourmet task continued and serviced the next
call in its entry queue for Make_A_Hot_Dog, and made a hot dog without mustard for Bill. At this
point Gourmet had completed all of its entry calls and allowed one of the other tasks to run. The task
named John was then allowed to continue execution. John ate his hot dog and requested another.
Once again, by some undefined method, the task named Gourmet was allowed to run and make a
second hot dog for John, with mustard, when Bill still hasn't been allowed to eat his first one. This
continues until all conditions have been satisfied, which means that five hot dogs have been made,
and all five have been consumed. Both consuming tasks declare their lack of hunger, and the task
named Gourmet declares that it is out of hot dogs. The program has finally run to completion.
WHAT ABOUT THE FUNNY ORDER OF RESULTS?
Even though the results seemed to come out in a funny order, they did follow all the rules we set
down for them to follow. Remember that as an experienced programmer, you are accustomed to
seeing everything come out in a very well defined precise order, because you have spent your
programming career writing sequential programs. If you look at this output from the point of view of
each task, you will see that the output from each task is perfectly sequential, as defined by the logic
of the task. Additionally, you will see that the order of execution has been preserved as defined by the
various rendezvous, because nobody eats a hot dog before it is made, and there are no hot dogs made
too early. The synchronization of the tasks has been done exactly as we requested.
Spend enough time studying the logic here to completely understand what is happening, then compile
and execute this program to see if your compiler does anything in a different order.
THE select STATEMENT
Example program ------> e_c27_p4.ada
Examine the program named e_c27_p4.ada for our first example program using a select statement.
The select statement is used to allow a task to select between two or more alternative entry points. In
effect, a task can be waiting at two or more entry points for an entry call to be made, and can act on
the first occurring entry call. The structure of the select statement is given by;
select
accept ...; -- Complete entry point logic
or
accept ...; -- Complete entry point logic
or
accept ...; -- Complete entry point logic
end select;
and is illustrated in lines 23 through 33 of the present example program. In this case there are two
select alternatives, but there is no limit to the number of selections that can be included. Each
additional branch is delimited by the reserved word or. When program control of the task arrives at
the select statement in line 23, either entry call can be accepted and acted upon immediately.
THE OVERALL PROGRAM
Common sense tells us that we cannot deliver a hot dog until we stock the shelf with a hot dog, so the
program has been written to reflect this. The task requires an entry call to Stock_With_A_Hot_Dog
before it begins the loop with the select in it to assure that at least one hot dog will be available. After
that, it doesn't care what the order of entry calls is because the select statement in the loop will allow
them to occur in any order. This is a very simplistic approach to setting up a precedence requirement
in an Ada task, but it is too simple to really be effective which we shall see when we examine some
of the problems that can occur.
In the first place, if the main program, or task, fails to call Stock_With_A_Hot_Dog first, the system
will simply lock up with the calling program demanding a delivered hot dog and steadfastly refusing
to continue until it does, and the called task refusing to deliver one until it has been stocked with one
in line 16. The system is in deadlock with both tasks refusing to do anything. You can simulate this
condition by reversing the two calls in lines 39 and 40. Your compiler will probably give a message
indicating deadlock has occurred and terminate operation of the program. Another problem has to do
with the inflexibility of this program, since we have once again counted the number of calls required
to complete the two tasks and programmed compatible numbers in the two tasks.
A further problem involves the fact that, after one hot dog has been stocked, there is nothing to
prevent us from taking delivery of hundreds of hot dogs without adding any more to the shelves.
When you think you understand this program, compile and execute it, then we will go on to the next
program where we will solve two of the three problems mentioned in connection with the present
program.
SELECT STATEMENTS WITH GUARDS
Example program ------> e_c27_p5.ada
Examine the program named e_c27_p5.ada for an example of a select statement with guards. The
guards are used to guard the entry points of the select statement to prevent the kinds of silly things
that happened in the last program. The task body Retail_Hot_Dogs has been modified in this
program to include guards in lines 26 and 34 for the select statement in lines 25 through 39. A guard
is simply a BOOLEAN condition that must be satisfied before that particular entry point can be
accepted and its logic executed. The general form of the select statement with guards is given as;
select
when <BOOLEAN condition> =>
accept ...; -- Complete entry point logic
or
when <BOOLEAN condition> =>
accept ...; -- Complete entry point logic
or
when <BOOLEAN condition> =>
accept ...; -- Complete entry point logic
end select;
and there is no limit to the number of permissible selections, each being separated by the reserved
word or. In fact, one or more of the selections can have no guard, in which case it is similar to having
a guard which always evaluates to TRUE. When the select statement is encountered, each of the
guards is evaluated for TRUE or FALSE, and those conditions that evaluate to TRUE are allowed to
enter into the active wait state for an entry, while those that have guards evaluating to FALSE are
not. Those with guards evaluating to FALSE are treated as if they didn't exist for this pass through
the loop. Once the guards are evaluated upon entering the select statement, they are not reevaluated
until the next time the select statement is encountered, but remain static.
LIMITING THE NUMBER OF HOT DOGS ON THE SHELF
In this program, when the select statement is entered in line 25, the guard at line 26 is evaluated and
if the number of hot dogs on the shelf is less than 8, then the accept statement in line 27 is enabled
and we are permitted to stock the shelf with one more hot dog. If the number of hot dogs is greater
than zero, as the guard at line 34 tests for us, then the accept statement in line 35 is enabled and we
are allowed to deliver a hot dog. Even though we may be allowed to either stock the shelf with a hot
dog, or deliver a hot dog, we must wait until some other task requests us to do so before we actually
do one of the operations. It should be clear to you that in this particular case we will always be
permitted to do at least one of the operations, and in many cases both will be permitted. If none of the
guards evaluate to TRUE, then none of the selections can be taken, and the program is therefore
effectively deadlocked and the exception named Tasking_Error will be raised. You, the
programmer, can trap this exception in much the same way that you can trap any other exception and
handle it in your own manner, but the rules are a little different for tasking exceptions than for
exceptions raised during sequential operation. We will cover tasking exceptions in detail later.
It should be pointed out that, even if a guard evaluates to FALSE, entries can be added to the entry
queue and serviced during subsequent executions of the select statement when the guard may become
TRUE. Because of this method of defining the entry queue, no calls to the entry are lost, and the
operation is predictable.
This program contains four tasks, counting the main program, with one named Five_Dogs stocking
the shelf very quickly with five hot dogs, because of the short delay, and another removing five hot
dogs a little slower. The main program stocks and retrieves four hot dogs rather slowly due to the
relatively long time delay built into the loop.
WATCH THE GUARDS DO THEIR JOB
When you run this program you will see very little action with the guards because of the selection of
the guard limits. The five hot dogs are put on the shelf very quickly, but the upper limit of 8 is never
reached, and there are always hot dogs on the shelf to supply the limited demands. In fact, as listed in
the result of execution, there are never more than 5 on the shelf, and always more than zero. You
should compile and execute the program to see if your compiler does the same thing as the one used
for this execution.
Change line 26 so that the limit is 3, and recompile and execute the resulting program. In this case,
you will very clearly see that the first guard prevents more than three hot dogs from being placed on
the shelf. In effect it builds up the entry queue for the Stock_With_A_Hot_Dog entry point and
requires the suppliers to wait for shelf space.
Reverse the delays in lines 46 and 54, as your next exercise, so that the hot dogs are consumed much
faster than they are stocked so that the guard on the entry point named Deliver_A_Hot_Dog will be
needed to protect the delivery of too many hot dogs. In this case, the queue to this entry point will
build up a list of requests to be satisfied as hot dogs are delivered.
THIS PROGRAM IS MUCH BETTER
This program solved two of the three problems listed concerning the last program but we still must
use the method of counting the required entry calls and providing the proper number of entries. As
promised before, this problem will be remedied soon. Be sure you compile and execute this program
three times, once unmodified, and twice with the suggested changes, then study the output to assure
yourself that you understand it completely.
A PROTECTED AREA OF MEMORY
Example program ------> e_c27_p6.ada
The example program named e_c27_p6.ada gives a very simple example of data protection. If you
have several tasks writing to the same record, it is conceivable that before one is finished writing,
another task gets control and begins writing to the same record. This would result in corrupted data
and is a major problem when writing programs with multiple tasks. The protected area in lines 8
through 39 defines three procedures that operate just like any other procedures we have worked with
in this tutorial, with the exception that they are protected from multiple entry. It is impossible for
more than one task to be executing code within these three procedures at once because that is their
purpose. If more than one task requests a call into these three procedures, one will enter the procedure
it called, and the others will be made to wait outside of the procedure they called until the first one
leaves the procedure it called. This prevents data corruption.
The remainder of the program is trivial, being composed of three tasks plus the main program that
work together to simply add and subtract animals from the common pool. Note that all data must be
in the private part of the specification, none is allowed in the protected body of the protected code.
FUNCTIONS ARE A LITTLE DIFFERENT
The procedures are allowed to read or write to the private data, but a function is only allowed to have
in mode parameters and it is only allowed to read the data in the private section. Since they are only
allowed to read data, multiple tasks are permitted to read the private data at the same time, but not
while another task is executing code in a procedure, since it may be writing to the internal data. This
permits multiple calls to read which solves the classic problem of readers and writers.
The protected section has a lot more flexibility than we are covering here, but this will give you a
good start in understanding what it is used for.
ONE MORE RESERVED WORD
The reserved word requeue is used within a protected block to call another subprogram on behalf of
the calling program. The conditions may not yet exist for execution of the desired code, so it is
requeue'd until the conditions are right. This is a very advanced technique that you can study on your
own when you need it. It will probably be a long time before you reach the level of expertise needed
to effectively use this technique.
PROGRAMMING EXERCISES
1. Move the end of the accept statement in e_c27_p1.ada to the line immediately after the accept
statement itself to see that it is possible to eat the hot dog before it is made because the tasks
are both running at the same time.(Solution)
2. Add another task to e_c27_p5.ada that executes a loop 10 times with a 0.3 second delay that
outputs the current number of hot dogs on the shelf.(Solution)
3. Using the package Ada.Calendar, output the elapsed time each time the new procedure
defined in exercise 2 outputs the number of hot dogs on the shelf.(Solution)
Advance to Chapter 28
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 28
THE CONDITIONAL RENDEZVOUS
IMPATIENT PROGRAMMING
Many times in your life you wish to do something but you decide after trying for a short period of
time, that it cannot be done, so you change your mind and do something else. This capability must be
available in a computer program also, since a computer program is often used to simulate something
in real life. This chapter will give examples of impatient programs, those that refuse to simply wait
forever for a certain job to be done.
THE BASIC PROGRAM OUTLINE
Example program ------> e_c28_p1.ada
Examine the file named e_c28_p1.ada for the program that will serve as the outline for the selective
rendezvous even though it does not contain one. The program consists of four tasks, one each to
describe Bill's day and John's day, and one each to describe eating a meal at a restaurant and at a fast
food Burger Boy (hopefully not an actual restaurant). In this program, all of the delay times have
been defined in terms of a constant named HOURS which actually causes a delay of one second per
hour to speed up program execution to a reasonable level.
Eating a meal at the restaurant requires half an hour to be served and another half hour to consume
the meal, whereas the Burger Boy requires only one tenth of an hour to be served and another tenth to
consume the meal. The task named Bills_Day has him eating three meals only one hour apart, and the
task named Johns_Day has him eating two meals in rapid succession then waiting four hours until he
eats supper. Another interesting point is the fact that both eat every meal at the restaurant with no
concern for how long they have to wait to be seated and eat. It is of no concern either to the Ada
program at hand or the analysis of the problem, that one of the tasks is never called, because this is
perfectly legal. Finally, both the restaurant and the Burger Boy can only serve one customer at a time,
since this is the way the tasks were programmed. They can however, each serve a different customer
at the same time.
EATING MEALS IS VERY SLOW
You will notice, by inspecting the delays, that John waits 0.4 hours and gets to the restaurant first,
then takes a total of one hour to eat. Bill waits one hour, and goes to the restaurant, but must wait on
the entry queue for 0.4 hours for John to finish eating, then takes another hour to eat. During this
hour, John has returned and is waiting to be served again. They continue getting in each others way
and finally each consumes three meals.
DEADLOCK OCCURS
The observant student will notice that the tasks named Bills_Day and Johns_Day each run through
sequentially to completion and make no additional calls. The two named Restaurant and
Burger_Boy however, each consist of an infinite loop and never actually complete. When the first
two tasks execute to completion, the system will recognize that there are no tasks running that are
capable of making calls to the waiting accept statements. The system will recognize this condition,
and it will terminate the program due to deadlock after displaying a message that deadlock has
occurred.
We still have not given an acceptable method of terminating our program gracefully, but we will later
in this chapter. You should spend the time necessary to thoroughly understand this program since it
will be the basis for the remaining example programs in this chapter. When you do understand it,
compile and execute it to see if your compiler will recognize the deadlock condition.
THE CONDITIONAL RENDEZVOUS
Example program ------> e_c28_p2.ada
Examine the program named e_c28_p2.ada for our first example of a conditional rendezvous. This
program is identical to the last one named e_c28_p1.ada except for the addition of a few statements
in the task named Bills_Day. In this program Bill is in a bit of a hurry to eat his first two meals and
puts in a few conditions to indicate this.
THE SELECTED RENDEZVOUS
When Bill starts his day, he is very impatient and will not wait at all for his first meal. If the
restaurant is not ready to serve a meal immediately, he will go to the Burger Boy and have his
breakfast. The statements in lines 29 through 33 replace the single statement of the last program to
indicate this impatience. In Ada terms, if the task named Restaurant is not waiting at the
Eat_A_Meal entry point, the call will not be made there, but a call will be made to the entry point
named Eat_A_Meal in the Burger_Boy task. If the Burger_Boy task is not waiting at the entry
point, then Bill will be required to wait until it is ready, when he will be served.
The reserved word else is used to indicate the selected rendezvous and it can follow as many or
clauses as desired. The general form is;
select
<entry call>;
or
>entry call>;
else
<entry call>;
end select;
The else clause will be taken if none of the other entry points are waiting at their respective
rendezvous points.
THE DELAYED RENDEZVOUS
Bill is not quite as hungry for his second meal so he is willing to wait for a short period at the
restaurant, but if he is not served within one tenth of an hour, he will go to the Burger Boy for lunch.
The single statement in lines 35 through 40 replaces the single statement in the previous program. In
Ada terms, the task named Bills_Day will wait .1 hour for the Restaurant task to reach its entry
point, after which it will call the entry point of Burger_Boy and wait no matter how long it takes to
be served there.
As many selections as desired can be used, with delay statements in as many of the selections as
needed. The general form is given by;
select
delay <time>;
<entry call>;
or
delay <time>;
<entry call>;
or
delay <time>;
<entry call>;
end select;
An else clause cannot be used if any delay statements are used because the else clause will be
executed immediately and any statement with a delay would never have a chance to time out. It
would therefore never be executed and must be regarded as unexecutable code.
BILL'S STUBBORN RESOLUTION FOR SUPPER
Bill has decided that under no conditions will he eat supper at the Burger Boy. He will wait for as
long as necessary in order to be served at the restaurant, and this is reflected in the single call in line
42 in exactly the same manner that was indicated in the previous program.
Take careful notice that both of these select statements are in the calling task, not the called task, but
we will see shortly that the same sort of things are available in the called task.
Examination of the result of execution will reveal that Bill did eat both of his early meals at the
Burger Boy. In addition, you will see that his second meal was ordered and eaten at the Burger Boy
during the time that John was eating his meal at the Restaurant. This indicates that resources are
being well used.
Be sure to compile and execute this program. Observe the result of execution to see that it does send
Bill to the Burger Boy on occasion. Because we still have no graceful termination, deadlock occurs
once again.
THE DELAYED ENTRY POINT
Example program ------> e_c28_p3.ada
Examine the program named e_c28_p3.ada for yet another addition to our example of Bill and John
satisfying their desire for nourishment. In this case, the restaurant will not remain open forever, nor
will the Burger Boy, both choosing to close for the day if there are no customers for a period of time.
The select statement in lines 59 through 72 contains two alternatives, one to handle a customer as
usual, and another with a delay of 1.5 hours followed by a restaurant closing statement. If there are no
entry calls to the entry named Eat_A_Meal for 1.5 seconds (since HOURS is actually one second),
then the second branch of the select statement will be executed, resulting in the display of a message
and execution of an exit statement. You will recall that an exit will take you out of the most
immediate loop and continue execution of statements from that point. In the case of the Restaurant
task, the task is therefore completed and waits for the other tasks to complete.
The task named Burger_Boy has the same alternative entry point, except for a delay of 2.1 seconds.
It also reaches its end statement and waits for the others to finish. Examination of the result of
execution will reveal that both eating establishments actually did reach timeout and closed for the day
leaving John to go hungry since he never ate his third meal. Since John did not eat his third meal of
the day, some code was not executed and it was not reported as such. This seems to be an error, but it
is actually not, because it is a tasking error that is not propagated to the main program. This is
according to the definition of Ada and is explained more fully in the last two paragraphs of this
chapter.
Be sure to compile and run this program and observe the early closing of the eating establishments.
ORDERLY TERMINATION OF TASKS
Example program ------> e_c28_p4.ada
Examine the program named e_c28_p4.ada for an example of orderly termination of tasks. This
program is identical to the first program in this chapter named e_c28_p1.ada except for two minor
changes. The entry point for the task named Restaurant has been put inside of a select statement in a
manner similar to that done in e_c28_p2.ada or e_c28_p3.ada but a different or clause is used, one
that contains the reserved word terminate. Each time through the loop, the entry point named
Eat_A_Meal can be selected for execution and the task execution will wait until this entry point is
called. While the program is waiting for the entry call, the branch of the select with the terminate
allows the possibility of a termination to occur. However, the termination can only occur under the
right conditions. The right conditions require that all other concurrent tasks, including the task in the
main program, are either at their final end waiting to terminate, or have a similar terminate
alternative to select.
The task named Burger_Boy has a similar terminate alternative. When the two calling tasks reach
their final end, the two serving tasks have terminate alternatives available, so there is an orderly
termination and the program returns to the operating system. Be sure to compile and execute this
program, then study the result for conformance to this definition.
THE abort STATEMENT
The abort statement will unconditionally abort any tasks with no regard to their present operating
condition. It is extremely abrupt and does not provide for an orderly termination. It should therefore
be used only in conditions of an emergency nature. Possibly the detection of loss of power would be
such a circumstance. The tasks to be aborted are listed following the reserved word abort in an
executable statement anywhere that it is legal to use an executable statement.
TWO TASK ATTRIBUTES
There are two useful task attributes that can be used to find the condition of any given task. They are
CALLABLE, and TERMINATED, and each returns a BOOLEAN type indication of the current
status of the task to which the attribute is appended. See the ARM for details on the use of these two
attributes.
TASK EXCEPTIONS
The tasking exception named Tasking_Error is raised during essentially any communication error.
Any error during task elaboration will raise this exception (this will be illustrated in a later example
program). This exception is raised in the caller if the called task is aborted, and is raised in the
originators of all calls on an entry queue of an aborted task.
If an exception is raised during a rendezvous, it is propagated into both tasks since each may need
some form of recovery. If the exception is not handled internally by the task, it is not propagated to
the calling program because to do so would be very disruptive. This means that a program that
appears to be working correctly could actually have a very disruptive tasking error, much like the
example program named e_c28_p3.ada in this chapter. Each significant task should therefore have an
exception handler to guard against the occurrence of an unknown exception.
PROGRAMMING EXERCISE
1. Modify the program named e_c28_p2.ada such that it has an orderly termination upon
completion.(Solution)
2. Modify e_c28_p2.ada even further such that John eats every meal at the Burger Boy unless he
has to wait there, in which case he eats at the restaurant.(Solution)
Advance to Chapter 29
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 29
ADDITIONAL TASKING TOPICS
We have covered most of the topics concerning tasking in the last three chapters, but there are a few
more tasking themes that must be considered in order for you to use tasking to the fullest. The topics
in this chapter are definitely advanced tasking topics and you may wish to ignore some of this
material until you have gained considerable experience in the use of Ada. It would be worth your
time to at least read through this material once to gain some exposure to it and to realize that these
capabilities exist in the Ada programming language.
THE TASK TYPE
Example program ------> e_c29_p1.ada
Examine the program named e_c29_p1.ada for an example of the task type in Ada. This will seem
like a very strange construct, but if you spend time thinking about it, you will see that it does have a
place in an Ada program, and it can be very useful.
In all previous programs with tasking, we have declared a task specification followed by a task body,
but that is not the general case. It is actually a shorthand notation for declaring tasking and is
somewhat similar to an anonymous type. The more general case of task definition is to declare a task
type specification, then the task body, followed by a task that is of the declared type. In fact, the task
is declared in much the same way we have been declaring variables in Ada, the task name followed
by a colon and the task type. In the present program we declare the first task type specification in
lines 7 and 8, and the corresponding task body in lines 16 through 22. The task itself is declared in
line 13 where Cow is declared to be of task type SHORT_LINE. In fact, there are three tasks
declared in this line, the other two being named Dog and Pig.
WHAT DOES IT REALLY MEAN?
If we declared a data type, then used the data type to declare three variables, you would have no
trouble understanding what was happening, because you have been doing that all along in this
tutorial. It may be a little more difficult for you to understand, but we are doing the same thing with
the task, except that we are declaring a type of a section of code, then generating and executing three
copies of that code. It is the same as if we declared a task specification named Cow that was identical
to that given in lines 7 and 8, then a task body named Cow that was identical to that given in lines 16
through 22, and did exactly the same thing for the other two tasks named Dog and Pig. Instead of
duplicating the code three times we declared a pattern for the task then told the system to run three
copies of it.
The task itself is extremely simple, consisting of a loop with a zero delay, and a statement to output a
message to the standard output device, the monitor. The tasks themselves are declared in line 13
before the task body is declared, but this should pose no difficulty for you since we have used partial
declaration of subprograms and types before.
A fine point should be mentioned here. A task type is always of limited private type, which means
you can do no operations on it. Assignment and comparisons are not available with a limited private
type, and it makes sense to prevent these operations with tasks.
THERE ARE TWO TASK TYPES DECLARED
In addition to the task type named SHORT_LINE, there is another named LONG_LINE similar to
the first, which outputs only three lines until it completes, but outputs a much longer line of text. Two
copies of this type are declared and executed.
The end result of all of this is the generation and execution of five tasks all running in parallel with
no rendezvous between them except for the zero time delay statements which we discussed in an
earlier chapter.
WHEN DO THEY RUN?
We discussed in an earlier chapter how each of the tasks got started running and this is no different.
When all five tasks have been completely elaborated and are all waiting at their respective begin
statements, and when the main program arrives at its begin statement, then all of the tasks begin to
execute. Remember that there are six tasks counting the main program as a task. Likewise, when all
of the tasks, including the main program, reach their end points, an orderly termination is effected
and the program ceases to execute, returning control to the operating system.
Compile and run this program and you will see that all five tasks do indeed run as described above.
Add a few additional tasks in lines 13 and 14 to see how easy it is to add additional tasks once the
task type is completed. It would be well worth your time to study this program until you understand
this material, because it will be used in the next few example programs.
A TASK AS PART OF A RECORD
It will not be illustrated here, but it is possible to declare a task type variable as part of a record. Such
a record can be used to declare a task variable along with a few other variables for use by the task.
AN ARRAY OF TASKS
Example program ------> e_c29_p2.ada
Examine the program named e_c29_p2.ada for an example of an array of task types. This program is
identical to the last except for the addition of an array of tasks in line 13. The task name Cow is
declared to be an array of tasks named Cow(1), Cow(2), ... Cow(4), so there are four tasks of this
type running concurrently when execution begins. These four are in addition to the two of type
LONG_LINE as in the previous program. Nothing more needs to be said about this program except
for you to compile and execute it to see that you really do get all six tasks running in parallel with the
task in the main program. There are actually seven tasks running in parallel. It would be a snap to
increase the Cow array to 6 tasks and see nine running, the six in the array, the two which are the
other type, and the main program.
A TASK ACCESS TYPE
Example program ------> e_c29_p3.ada
Examine the program named e_c29_p3.ada for an example of an access type which can be used to
access a task. Once we have the ability to access a task through an access variable, we can also
dynamically allocate a task and execute it. We will illustrate this operation in the present example
program.
This program in nearly identical to the last two except that the variables named Elephant and
Hippopotamus are not task type variables, but task access type variables because we declared the
type LONG_POINT as an access variable in line thirteen.
When we execute this program, the three statically declared tasks begin executing when the main
program does, but the two access type variables do nothing because they are simply access types that
access nothing yet. The main program outputs a line of text to the monitor, then dynamically
allocates a copy of the task type LONG_LINE in line 37 and the new task begins executing. Another
task is allocated and begins execution in line 38. All five tasks will run to completion and there will
be an orderly termination.
In this case, the main program is a parent task to the two dynamically allocated tasks and they are
referred to as children tasks of the main program.
WHEN DO THEY BEGIN EXECUTION?
The biggest difference between this program and the previous two, involves when the respective
tasks begin execution. All of the statically declared tasks begin execution when the main program
begins, but the dynamically allocated tasks begin execution when they are allocated. This can be
clearly seen by studying the result of execution of each of the three programs. In the present program,
the first output of each of the two allocated tasks is delayed considerably behind the first output of the
statically declared tasks.
Be sure to compile and execute this program and compare the output of your compiler with that
generated by the author's Ada compiler. It would be a simple matter for you to add a few more access
variables and allocate additional tasks just to gain the experience.
A VERY INEFFICIENT EXAMPLE OF TASKING
Example program ------> e_c29_p4.ada
The example program named e_c29_p4.ada is an example of a very poor way to solve this particular
problem but is a meaningful example of a tasking solution to a problem. In this program we will
declare eleven parallel tasks, start all eleven running, then retrieve the results of the eleven tasks.
We begin by declaring several types and variables, then declare a task specification in lines 17
through 20 with two entry points. Because the constant EMPLOYEES is declared as having the
value 11, we declare 11 tasks in line 22, named Adding_Task(1), Adding_Task(2), ... Adding_Task
(11), each of which will begin running when we begin execution of the main program. The task body,
given in lines 24 through 39, consists of a local variable, and two accept statements, with a few
calculations between them.
The main program is the most interesting part of this program, but we need to get through the
initialization code before we come to the interesting part. The initialization code in lines 42 through
48, assigns values to the big array declared previously including a couple of funny data points which
will show up in the results. Finally, we display a message that the array is filled.
START ALL TASKS RUNNING
The loop in lines 53 through 55 calls the eleven tasks at their first entry point and begins them
executing so that they all are executing their respective for loops. If you had a computer with a large
number of actual processors, each task could be assigned to a separate processor and all calculations
could be done concurrently. In the present case of adding seven numbers, the calculations are trivial,
but if each task required the performance of several million floating point operations, the time saved
through the efficient use of parallel hardware could be very significant. This program serves to
illustrate the technique that could be used and is the most practical illustration of tasking to be
demonstrated in this course.
ALL TASKS ARE NOW COMPLETE
The loop in lines 58 through 61 once again calls all eleven tasks one at a time at their final entry point
where it requests that the result of the summation be returned. All results are stored in the
Weekly_Totals array, and the results are then displayed along with appropriate text. You will notice,
when you examine the result of execution, that employees numbered 2 and 3 have the funny data
reflected in their totals.
The point of interest you should have gleaned from this program is that all eleven tasks were
operating in parallel, with the main program being a twelfth task. The logic of the main program
requires concurrent tasks as opposed to eleven calls to a single subprogram. Keep in mind that this
would actually be a very poor way to program this particular problem, but was included as an
illustration.
Be sure to compile and execute this program on your system to verify that the results are the same.
After it does compile and execute correctly, change the number of employees to see how many tasks
can be operated in parallel with your system. You will not be limited by some arbitrary upper limit on
the number of allowable tasks imposed by your particular compiler, but by the amount of memory
required for each task.
PRIORITY OF TASKS
Example program ------> e_c29_p5.ada
The example program named e_c29_p5.ada contains an example of establishing priority within tasks.
The priority of tasks is indicated by the use of the pragma named PRIORITY as illustrated in the
task specifications and the declaration part of the main program. An integer class variable is included
in the parentheses with the larger numbers indicating the higher priority or the most urgent tasks. The
range of allowable priorities are defined for your individual compiler in the System specification
package definition as a subrange of INTEGER. According to the ARM, a range of 0..0 is legal. If
your compiler only supports a single value for the priority, you will have to change the priorities in
this program to the single value and you will, in effect, have no priority.
Compile and execute this program, then change the priorities and see what order of execution you can
achieve. It is important to realize that the ARM does not absolutely define how priorities will be
handled, but it only gives a rather vague definition. Priorities should be used with care in a production
program.
A FAMILY OF ENTRIES
Example program ------> e_c29_p6.ada
The example program named e_c29_p6.ada illustrates one more construct that can be used with the
tasking rendezvous to achieve a user defined priority structure. In this case, there are three entry
points which are very similar but have only a small difference between them. An enumerated variable
is declared and used as the discriminator between the three entries. The only real advantage to using a
family of entries is that they all use the same name and therefore add to the clarity of the resulting
program.
The use of the family of entries is illustrated here and should be very easy for you to understand.
When you do, compile and execute this program and compare your output with that given in the
result of execution section.
PROGRAMMING EXERCISES
1. Increase the size of the array in e_c29_p2.ada to see how big you can make this array before
the program no longer fits in memory. This will give you an idea of how many tasks can be
run at the same time with your system.(Solution)
2. Add another access variable to e_c29_p3.ada and initialize it as a part of its declaration. When
will this task begin execution?(Solution)
Advance to Chapter 30
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 30
GENERIC SUBPROGRAMS
GENERIC UNITS ADD TO ADA'S FLEXIBILITY
The concept of packages along with generic units gives Ada the capability to be used for general
purpose software components. Utility routines can be written once and used in several programs
eliminating the need to rewrite and debug the utility again and again. This ability would be greatly
diminished without the generic capability of Ada.
WHAT IS A GENERIC UNIT?
Example program ------> e_c30_p1.ada
A generic unit is a template for a subprogram or a package, but it cannot be executed directly. A copy
of the generic unit can be instantiated, and the resulting unit can be executed just as any other
subprogram or package. As with all other topics in this course, the best way to learn how to use this
new technique is through use of an example, so examine the program named e_c30_p1.ada.
A SIMPLE GENERIC PROCEDURE
The generic specification is given in lines 2 through 4 and the generic body is given in lines 6 through
12. A careful inspection of this procedure will reveal that there is no actual type defined for the type
listed as type ITEM. The purpose of using a generic procedure is to allow you to use the procedure
for any type you desire, within reasonable limits, without being forced to rewrite the procedure for
each specific type. In order to use this procedure in a program, we will supply a type which will be
substituted in the procedure each place where the type ITEM appears when we instantiate a copy of
the procedure.
The reserved word generic is used to mark the beginning of a generic unit, which may be a package,
a procedure, or a function. Between the reserved words, in this case generic and procedure, we
include a list of formal generic parameters which define the optional types, variables, and other
entities which will be used in the body of the procedure. In this case there is only one formal generic
parameter named ITEM, and it is constrained to be any type of the integer class of types. An
explanation will be given soon to define why the type of ITEM is constrained to be of the integer
class of types. For the time being, simply accept the statement as true.
The procedure specification in line 4 and the procedure body in lines 6 through 12 are no different
than any of the other procedures we have used throughout this tutorial, except for the fact that the
type named ITEM is undefined throughout the procedure. Since the type ITEM is undefined, the
procedure is unusable in its present form.
HOW DO WE USE THE GENERIC PROCEDURE?
In order to use the generic procedure, we must first tell the system to get a copy of it which we do in
line 18 using the with clause.
Referring to the main program, we declare a derived type of the integer class named MY_INT in line
24. In line 26 we declare a procedure named SwapInt and since we are using the reserved word new
after the declaration, it is defining an instantiation of the generic package named Exchange_Data for
use with type INTEGER. You will recall that instantiating the procedure means we create an
instance of the generic procedure, and in this case we are defining a procedure that can swap
INTEGER types of variables.
The type INTEGER is used in the resulting executable procedure each time the generic word ITEM
is used in the original generic procedure. The result would be exactly the same as making a copy of
the generic procedure, changing its name to SwapInt, then substituting the type INTEGER for each
appearance of the word ITEM. A call to SwapInt with any two INTEGER type variables will result
in exchanging their values.
WHAT GOOD DID THIS DO US?
Admittedly, we could have simply defined the procedure SwapInt in the first place and everything
would have been just fine. The real benefit comes from the next line of the program where we
instantiate another copy of the generic procedure named Exchange_Data, name it SwapNew, and
tell the system to use the type MY_INT as the replacement for the formal generic type named ITEM.
Line 27 is therefore equivalent to writing out the generic procedure once again for the new type we
declared earlier.
If we had a large number of different types of the integer class of variables, we could instantiate a
copy of this procedure for each with a single line for each, so the benefit would be very significant.
REUSABLE SOFTWARE
Once this procedure is written and debugged, it can be used in any number of programs because it
does not have to be modified for each new type we make up. Different programmers can use the
procedure in different packages once it is tested thoroughly, so each programmer does not have to
write it anew. There is a new industry developing in the software community. This industry
specializes in writing reusable software components in Ada that are written with the flexibility
afforded by the generic units.
The remainder of the program should pose no problem for your understanding, so you will be left on
your own to study it, then compile and execute it.
A FEW NOTES ABOUT GENERIC UNITS
This program could be split into two separate files with the first including the generic procedure in
lines 1 through 12, and the second including the using program in lines 16 through 56. The files can
be compiled separately, and once the generic procedure has been compiled, it never needs to be
compiled again, but can be included in the Ada library in compiled form. The generic procedure and
the main program were combined here for the sake of convenience.
ANOTHER NOTE ON COMPILATION UNITS
In some cases, the generic part can be split once again into two separate files, the first being the
generic specification in lines 2 through 4 and the other being the procedure body in lines 6 through
12. If they are split in this manner, the order of compilation must be maintained so that the type
checking can be done as described earlier in this tutorial. The Ada definition allows a compiler writer
to require the generic specification and body be included in the same file for his convenience, so it
depends on the particular compiler you are using to define whether or not you can split the generic
procedure into two parts.
Of course, with such a simple procedure, it is difficult to grasp the magnitude of the benefits of this
new Ada construct, but in a real programming situation, many cases of reusability will become
apparent and the benefits will be appreciated.
ADDITIONAL FORMAL GENERIC TYPES
Example program ------> e_c30_p2.ada
Examine the program named e_c30_p2.ada for two additional examples of generic subprograms. You
will notice that the first is a generic procedure and the second is a generic function, indicating to you
that either form of subprogram can be written as a generic unit. The first subprogram is nearly
identical to the one in the last example program, the only difference being in the declaration of the
formal generic type in line 3 which we will discuss shortly. The second subprogram, the function in
lines 16 through 25, will be simple for you to comprehend if you substitute, in your mind, the type
INTEGER for each occurrence of the type ANOTHER_ITEM. In fact, it averages the two values
given to it and returns the result.
WHY THE DIFFERENT TYPES?
In line 3, we declare the type ITEM as private, and in the function named Average we declare the
type ANOTHER_ITEM as a type of the integer class of types (we will show you how shortly, just
be a little patient). As with so many things, the type selected is the result of a compromise. If we
restrict the type to only the integer class, then in the generic subprogram, we can perform any
operations that are defined for the integer class of variables, such as arithmetic operations, all six
comparisons, and logical operations. On the other hand, we are restricted in the number of types that
the generic subprogram can be used for, since it cannot be used for any real types, records, or arrays.
In a sense, we have restricted the usage of the generic procedure by allowing more freedom of use
within the procedure.
The first subprogram declares the formal generic type to be of type private which severely limits the
operations that can be performed on the data within the procedure. The generic procedure is limited
to assignment, and compares for equality and inequality. No other operations are allowed within the
generic procedure. However, by limiting the type to private within the generic procedure, we allow a
much greater flexibility in the calling program. This generic procedure can be instantiated with any
type that can be assigned or compared for equality or inequality, which is essentially any type. By
limiting the operations allowed within the generic procedure, we have made it much more usable to
the caller.
HOW FLEXIBLE ARE THE GENERIC SUBPROGRAMS?
Jumping ahead to the main program for a few moments, we see that the generic procedure
Exchange_Data is successfully instantiated for the type INTEGER, MY_RECORD, and FLOAT,
in addition to a few others. This procedure is extremely flexible because it was declared with a very
restrictive (restrictive within the generic procedure itself) generic formal type. In addition, it makes
sense to be able to swap two elements of record types, or two integers, or two real variables.
Continuing in the main program, you will see in lines 57 and 58 that there are not nearly as many
uses for the function which was declared with the much more liberal (liberal within the generic
function itself) type. Since arithmetic operations on integers were permitted in the function, a record
type cannot be used in an instantiation of this generic procedure, because it makes no sense to add
two records together.
A SEEMINGLY UNNECESSARY LIMITATION
You will note that because of the way we declared the generic formal type, real types cannot be used
in an instantiation of the function. It would make sense to be able to take the average of two real
numbers, but it cannot be done with this function because of the definition of Ada. The reason is
because there is no generalized scalar type that can be of either integer or real. The addition of one
would add lots of extra baggage to the language and would not add much to the utility of Ada. The
language is already big enough without adding another burden to it.
AN INEFFICIENT THING TO DO
Lines 52 through 55 each instantiate a copy of the generic procedure Exchange_Data, and each uses
the same type for its instantiation, namely CHARACTER. This will result in four sections of code
that each do the same thing, the only difference being in the name by which the procedure will be
called. If there is in fact a basis for using different names for the same procedure the renaming facility
should be used because it will only create an alias for each new name. Only one section of code will
be required.
A REALLY DUMB THING TO DO
Line 59 contains an instantiation of the generic function Average that uses type INTEGER and
names it Swap, a name that has already been overloaded five times in lines 48 through 52. The Ada
system is smart enough to figure out what you really want to do by comparing types and recognizing
the fact that this is a function call, and it will do exactly what you want it to do. It would be very poor
practice to use a nondescriptive name for a function in any Ada program, but it would be especially
bad to use a name that has already been used for a different purpose. The Ada compiler would handle
this correctly, but you could have a really hard time deciphering it later.
As discussed earlier, this file could be separated into at least three different files and compiled
separately, and with some compilers, it could be separated into five files.
A QUICK REVIEW
Remember that when selecting the types for the generic formal parameters, if you select a type that is
very restrictive within the subprogram, the subprogram can be used with many types by the user. If
you select a type that is rather permissive within the subprogram, the resulting generic subprogram
cannot be used with as many types by the user. Be sure to compile and execute this program after you
understand the details of its operation.
WHAT ARE THE FORMAL GENERIC TYPES?
Example program ------> e_c30_p3.ada
Examine the program named e_c30_p3.ada for an example of nearly all of the available formal
generic types. We will study each one in succession in some amount of detail. Beginning with the
limited private type illustrated in line 3, we have no freedom within the subprogram, but it can be
used with any type in the calling program. The private type is very limited within the generic
subprogram, but allows nearly any type in the calling program.
THE DISCRETE FORMAL PARAMETER TYPE
The discrete formal parameter type is declared with the reserved word type followed by the type
name, the reserved word is, and the box "<>" within parentheses as illustrated. The type name used
here can be matched with any actual type which is of a discrete class. These types are integer class
types, enumeration types, the BOOLEAN, and the CHARACTER type. Within the generic
subprogram, any operation can be used which can be done to an enumerated variable. This explains
why it was necessary for the POS and ORD attributes to be defined for integer type variables as
mentioned in an earlier lesson. Because these attributes are available, the integer types can be used
with this formal generic type.
THE INTEGER CLASS FORMAL PARAMETER TYPE
The integer formal parameter type, as illustrated in line 6, is declared with the reserved word type
followed by the type name, the reserved words is and range, and the "box" as illustrated. The type
name used here can be matched with any actual type that is of the integer class. Within the generic
subprogram, the arithmetic operations are available, in addition to all of those operations available
with the discrete generic formal parameter. But as you may expect, a generic subprogram using this
generic formal parameter can not be instantiated with an enumerated type.
THE REAL FORMAL PARAMETER TYPES
The real formal parameter types are declared as illustrated in lines 7 and 8, with the reserved words
digits or delta indicating the floating or fixed variety of real types. Only operations permitted for
those types are permitted within the generic subprogram, and only the respective real types can be
used by the calling program when instantiating a copy of the generic unit.
A procedure that actually uses none of the types is given in line 9. The body for the procedure is
given in lines 11 through 14, but it is not very interesting because it doesn't do anything. The second
procedure, named ManyUsed, lists all of the types in a similar manner but declares a procedure that
uses all six for formal parameters as an illustration. The program itself uses little of the declared
interface.
The details of this program will be left for your study after which you should compile it. Because this
is composed of only generic subprograms, it is not executable.
THE ARRAY TYPE FORMAL GENERIC PARAMETER
Example program ------> e_c30_p4.ada
Examine the program named e_c30_p4.ada for examples of array type generic formal parameters.
The specification and body of a procedure with a constrained array type generic formal parameter are
given in lines 3 through 17. There are actually three generic formal parameters listed here, one on
each of the three lines numbered 4 through 6.
To define how this procedure is used, we will refer to the instantiation call in line 49 of this file
which happens to be within the declaration part of the main program. The generic formal name
SUBSCRIPT must be replaced with an actual parameter that is of any discrete type because the type
mark contains a "<>" in parentheses. Furthermore, the first element in the actual parameter list will
be substituted for this parameter because it is first in the formal parameter list. To begin our
instantiation requested in line 49, we can replace every occurrence of the word SUBSCRIPT in lines
4 through 17 with the word LIMIT_RANGE and we are on our way to creating a usable procedure.
The second parameter of the actual parameter list is the type named MY_FLOAT, and it will be used
to replace every occurrence of the generic formal type named FLOAT_TYPE listed in the second
line of the generic formal parameters. You will notice that the required type is any floating point type
because of the occurrence of the reserved word digits in line 5. The type of the actual parameter, as
listed in line 50, and defined in line 46 is a floating point type.
CONSTRAINED ARRAY GENERIC PARAMETER
The third line of the formal parameters, which is line 6, declares the constrained array with which a
given instantiation can be used. You will notice that this formal parameter depends on both formal
parameters declared previously, but since there is only one new parameter in this line, the Ada
compiler can properly assign the actual type FLOAT_ARRAY as the replacement for the formal
parameter named CONSTRAINED_ARRAY. If you actually make a copy of this generic procedure,
and replace the formal parameters with their corresponding actual parameters, then change the name
of the procedure to Sum_Array, you could replace the instantiation in lines 49 and 50 with the
resulting procedure and the program operation would be identical. Lines 64 and 65 give examples of
using the instantiated procedure and will be left to your study.
UNCONSTRAINED ARRAY GENERIC PARAMETER
Lines 22 through 34 give an example of use of an unconstrained array as a generic formal parameter,
and is nearly identical to the last example except that the index type is declared to be of type
POSITIVE rather than being flexible as in the last example. The type of the index variable could be
declared as a generic type parameter with another line added prior to line 24, and the instantiating
statement would require three types instead of only two. Line 60 is the example instantiating
statement and lines 67 and 68 give examples of use of the resulting function.
As mentioned several times before, each of these subprograms could be separated into separate files
and compiled separately, then used by any calling program. Be sure to compile and execute this
program.
THE ACCESS FORMAL GENERIC PARAMETER
Example program ------> e_c30_p5.ada
Examine the program named e_c30_p5.ada for our first look at the last generic formal parameter
type, the access type. The first procedure, in lines 2 through 14 use an access type that matches only
an access type which accesses an INTEGER, resulting in little or no flexibility since an access type
to an INTEGER type variable is used infrequently. A copy of this generic procedure is instantiated
in line 45 of the main program, and the procedure is exercised in line 76 as an example for your
study.
The second procedure, given in lines 19 through 32 illustrates a much more useful procedure because
this procedure can be used for any access type, including the composite types of arrays and records.
Three copies of the generic procedure are instantiated in lines 64 through 68 and all three are
exercised in the main program. Note that the more general procedure is used in line 77 to transpose
the INTEGER type variables back to their original positions.
The details of this program should be simple for you to follow, so no additional comment needs to be
made. Be sure to compile and execute this program.
PROGRAMMING EXERCISES
1. Add a subtype of INTEGER to e_c30_p1.ada and determine what happens if you instantiate a
copy of Exchange_Data for the subtype in addition to the instantiation for INTEGER. Add
statements to the main program to swap values of the new type.(Solution)
2. Attempt to instantiate a copy of Average that uses type FLOAT in e_c30_p1.ada.(Solution)
3. Attempt to perform an addition in the body of Exchange_Data in violation of the private
type.(Solution)
Advance to Chapter 31
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 31
GENERIC PACKAGES
OUR FIRST GENERIC PACKAGE
In the last chapter we studied the use of generic subprograms. This chapter will be devoted to the
study of generic packages, and we will exhaust the topic because only procedures, functions, and
packages can be used as generic units.
Example program ------> e_c31_p1.ada
Examine the file named e_c31_p1.ada for our first example of a generic package. This package is
named EasyPkg, because it is so easy to understand, and is composed of one procedure and one
function. The generic package begins with the reserved word generic, followed by two generic
formal parameters, and the standard format for an Ada package specification in lines 5 through 9. The
first generic formal parameter is a discrete type, and the second is a floating point type as we
discussed in the last chapter.
The instantiating statements are given in lines 39 and 41 of the main program and are no different
than those used in the last chapter except for the first word, the reserved word package, because we
are declaring an instance of a package in this case. If we replace the formal parameter types with the
types declared in each instantiation statement, we will have a normal package just as we studied
previously. In the case of a package, we can add the use clause as we have in lines 40 and 42,
eliminating the need for the extended naming notation, commonly called the dot notation. After
declaring a few variables to work with, we are ready to exercise the two procedures and functions we
have declared. It should be clear to you that we have a procedure and a function in the package
named Funny_Stuff, and another procedure and function in the package named Usual_Stuff.
A fine point must be mentioned about the use clause when used with the generic package. It is not
legal to use a use clause with the generic package itself. Every instantiation must explicitly give the
extended name for the generic package. Of course this only occurs with nested generic packages
which are the topic of the next example program. The use clause however, is legal for use with any
and every instantiated copy of a generic package. The instantiated package's new name cannot be
overloaded because it is not permitted to overload the name of any package.
USING THE INSTANTIATED PACKAGES
In line 53, we call the procedure named Trade_Values, but since there are two procedures with this
name, Ada will pick the correct one by comparing the types. This is our old friend called overloading
again. In line 54, because of the types used, the other procedure will be used as indicated in the
comments. In lines 55 and 56, we explicitly tell the system which overloading to use, but it will still
check the types to see if they are compatible. In line 57 we tell the system to use the wrong one which
leads to a type mismatch and a compile error, and in line 58, we use two different types for the
parameters, which is another type mismatch. (Lines 57 and 58 are commented out so that we will not
actually get the error, but you should remove the comments to see that the compiler does report an
error.)
Lines 60 through 64 give examples of proper usage of the two functions instantiated above and
illustrate that the literals of type universal_real are compatible with both copies of the function as you
would expect. The compiler uses the type of the assignment variable on the left hand side of the
assignment statement to decide which overloading to use for each statement.
After you spend enough time to understand this program, compile and execute it even though it has
no output.
NESTED GENERIC PACKAGES
Example program ------> e_c31_p2.ada
The example program named e_c31_p2.ada contains a generic package nested within another non-
generic package. The outer package contains a single procedure, and the nested generic package
contains a single formal generic parameter of a floating point type, and a single function. The
procedure and function are the same as those in the last program, but they are organized differently
here for illustration.
Note carefully that since the procedure is not in the generic part of the outer package, it is directly
available in the same manner as if it were in a package without a generic part. The package name can
be declared in a use clause which will make the non-generic portion of the package usable.
The executable part of the program is very simple and very similar to the last example program so it
will be left to the student to study, compile, and execute this program.
OBJECTS AS GENERIC FORMAL PARAMETERS
Example program ------> e_c31_p3.ada
Examine the program named e_c31_p3.ada for an example of using objects for formal generic
parameters instead of just types. In this case the number of ROWS and the number of COLUMNS
will be part of the instantiation, and the resulting procedure and function will be ready to work with
the desired size matrices. In addition, the constant named ROWS, and the constant named
COLUMNS are initialized to the values given in lines 4 and 5. If a value is not given with the
instantiation, these values will be used in much the same way that we use default values with a
procedure or function.
In this case, the package body uses the standard Ada.Text_IO package and outputs a string to the
monitor each time one of the subprograms is called. This is only done to illustrate to you that there is
nothing magic about the package Ada.Text_IO, and that it can be used within another package.
USING THE OBJECTS
In line 58, the package is instantiated using the positional aggregate notation with values of 3 and 5
for the matrix size. Line 60 illustrates the use of the defaults declared in the generic part above, and
line 61 illustrates the use of the named aggregate notation used during package instantiation.
AN EXPORTED TYPE CAN BE USED
Referring back to line 8, we have the type named LOCAL_MATRIX declared in the package
specification and therefore available to any calling program. If you refer to the private part of the
package specification, you will see that the type LOCAL_MATRIX is declared to be a function of
the formal generic objects. After we instantiate a copy of the package, we have the exported type
available for use in the calling program. We use the types exported types in lines 66 through 68 to
declare a few variables, but even though two of the instantiations make use of the use clause, we
must explicitly declare the desired type by using the extended naming notation. This is because the
compiler has no way of knowing which exported type we are interested in using for each variable.
With the above descriptions of the new concepts in this program, you should be able to understand
the details of it. Be sure to compile and execute this program.
A PROCEDURE AS A GENERIC PARAMETER
Example program ------> e_c31_p4.ada
Examine the program named e_c31_p4.ada for an example of a procedure being used as a generic
formal parameter. The syntax uses the reserved word with to begin the formal parameter in line 4,
and the complete header for the procedure is given with the list of formal parameters and their types.
This procedure can then be called from within the body of the package and will refer to a procedure
that is actually outside of the generic package. Now we must define the procedure that will be called
in order to satisfy a call to this formal parameter. We will see where this procedure is defined shortly.
Refer to the main program where the procedure named R_Average is declared. It has exactly the
same formal parameter structure as that declared in the generic formal parameter, so it can be used in
the instantiating call in line 43. A call to the procedure in the generic instantiation actually results in a
call back to the procedure in the calling program to do some calculations.
WHAT CAN THIS BE USED FOR?
This capability gives you the ability to write a generic package that can be used in several places but
with a slight difference in each place, because each instantiation can use a different procedure for the
reference back to the calling program. This is simply another one of the available entities that add to
the flexibility of Ada. After you understand the logic here, you should compile and execute this
program.
A FUNCTION AS A GENERIC PARAMETER
Example program ------> e_c31_p5.ada
Examine the program named e_c31_p5.ada for an example of using a function as a generic formal
parameter. The logic is similar to that described in the last program except that a function is used for
the reference back to the calling program. No further explanation will be given since it should not be
needed. It should not come as much of a surprise to you that several procedures or functions, or a
combination of them, can be used as generic formal parameters. Any of the isolated examples can be
combined as needed to achieve the desired goal. Nesting of generic and normal packages can be done
as needed. It will be up to you to decide how to modularize and package your program to best
accomplish the stated goals. Ada gives you many options. Be sure to compile and execute this
program.
PROGRAMMING EXERCISES
1. Add an enumerated type to e_c31_p1.ada, instantiate a copy of EasyPkg, and use it to swap
some values of the enumerated type. You will have to supply a floating point type with the
enumerated type when you instantiate it.(Solution)
2. Convert e_c16_p1.ada from chapter 16 of this tutorial into a generic package that can be used
with any discrete type. Modify e_c16_p2.ada from the same chapter to instantiate and use both
an enumerated type and an INTEGER type.(Solution 1)(Solution 2)
Advance to Chapter 32
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 32
MACHINE DEPENDENT FEATURES
In this chapter we will cover some of the constructs available with Ada that give you the ability to get
into real trouble, because we will be using the low level features of Ada. The low level features are
those that allow us to get down to the inner workings of the computer, but we will be able to get to
them by use of rather high level Ada abstractions.
OVERRIDING COMPILER DEFAULTS
Normally, the compiler will make many decisions for us about how to store data, and how to operate
on it. Occasionally, we wish to tell the compiler that we are not satisfied with the way it defaults
something, and we wish for it to use a different means of representation. The topics examined in this
chapter will give us the ability to tell the compiler how to map something onto the underlying
hardware. We gain control of how the compiler represents some things within the machine, but we
also may make the resulting program nonportable. This can cause many problems if we ever wish to
move our program to another computer. In addition, we may affect the size or speed of the resulting
code, since the compiler writer will use a certain method of representing data because it results in
some form of savings on the particular target machine.
USE REPRESENTATION CLAUSES SPARINGLY
In general, the use of the representation clauses which will be discussed in this chapter, should be
used very sparingly if they are used at all. In any case, it would be best to delay using any of these
constructs until the program is fairly well developed, because correct operation of the overall
program is far more important than generating tight efficient code. After the program is debugged
and operating as desired, it is usually a simple matter to go back and tighten up the size and speed of
the most heavily used sections of code. You may find that after you get the program working in a
macro sense, it is fast enough and compact enough that it is not necessary to resort to these
techniques.
With all of these words telling you not to use these programming techniques, we will now take a look
at how to use some of them. Keep in mind however, that when you use these Ada constructs, you
may lose the ability to port your program to another implementation. You will also find that the
example programs in this chapter are those that are most likely to cause problems with your compiler.
THE REQUIRED PACKAGE NAMED System
According to the Ada 95 reference Manual (ARM), your compiler must have a standard package
named System which must be defined in Annex M of the documentation supplied with your
compiler. Section 13.7 of the ARM contains a minimum list of items that must be defined for you in
the package specification for System. It would be profitable for you to spend a few minutes studying
the definition of the package named System in your compiler documentation and the required items
listed in the ARM. You will find several constants defined there that you have been using throughout
this tutorial, and now you know where they came from.
WHAT REPRESENTATION CONTROLS ARE THERE?
When we attempt to control the representation of data and the way it is stored, we are actually telling
the Ada compiler how to define a type. It would be a little more precise to say we are telling the
compiler how to modify its default method of storing a certain type, including which parameters we
want it to change, and what to change them to.
There are four different areas of Ada typing that can be specified with representation clauses, and we
will look at each area in succession. They are listed in no particular order as follows.
Length specification
Record type representation
Enumeration type representation
Address specification
You will note that in each example, we will first declare the basic type, then we will tell the Ada
compiler what parameters we wish to modify to suit our purposes, and finally we will declare objects
of the modified type. We will mention this order again in some of the following example programs.
THE LENGTH SPECIFICATION
Example program ------> e_c32_p1.ada
The length specification is used to declare how many bits can be used to store data in a certain type.
This representation clause is illustrated in the example program named SMe_c06_p1.ada, which you
should examine at this time.
The only code that is of special interest in this program is found in lines 7 through 10. First we define
a constant of value 1 named BITS to be used later for a very good reason. Next we declare a derived
type which covers a range of -25 through 120, a range small enough to be represented with only 8
bits. Since we wish to declare a rather large array of this type, and we suspect that our compiler will
simply assign a full word of 32 bits to each variable of this type, we tell the compiler that we want it
to use only 8 bits to store a variable of this type. Line 9 is a representation clause to do this. It begins
with the reserved word for followed by the type with a tick and the word SIZE. It looks like an
attribute, and that is just what it is, because we are telling the compiler that we want the attribute
named SIZE to have the value of 8.
The use of the constant should now be clear. It makes the expression extremely clear because when
you read the expression it says just what it is doing. We told the compiler to use 8 bits for the size of
this type. You should not be bothered that using this construct will slow down the program, because it
will not. This constant will be evaluated only once, and that will be at compile time, not when the
program is executing.
YOUR COMPILER MAY NOT LIKE THIS CLAUSE
The ARM does not require that an Ada compiler implement every representation clause. For this
reason, even though you have a validated Ada compiler, it may not implement line 9. If it doesn't, it
will give you a message during compilation that it cannot handle this representation clause and will
fail to give you an object module. You will be required to remove the offending representation clause
and recompile the program. If your compiler does accept it, when you execute this program you will
see that the type SMALL_INTEGER requires only 8 bits of storage. After successfully compiling
and running the program, comment out line 9 and compile and execute it again. You will probably
find that your compiler requires more than 8 bits to store data of this type. If your compiler cannot
handle line 9, comment it out and compile and execute the resulting program.
Remember that at the beginning of this chapter we stated that of all the programs in this tutorial, this
chapter would contain the ones that were most likely to have problems with your compiler. This is
the reason that so many of these programs are not transportable from compiler to compiler. Only
three of the five compilers used to test these example programs implemented this particular clause.
THE STORAGE_SIZE REPRESENTATION CLAUSE
Another representation clause that is very similar to SIZE is the one named STORAGE_SIZE. This
is used to tell the compiler how many storage units to use to store an access type variable or a task
type variable. The ARM is not very specific on just what a storage unit is, so it must be defined by
your compiler. Because it is not well defined, and is therefore different for each compiler, an
explanation may be more confusing than simply not attempting to explain it. You will be left to study
it on your own, remembering that it is similar to SIZE. With all of the Ada you have studied to this
point, you should be able to easily decipher the notes on this topic in your compiler documentation.
THE RECORD TYPE REPRESENTATION
Example program ------> e_c32_p2.ada
Examine the program named e_c32_p2.ada for an example of two additional low level constructs, the
record type representation and the unchecked conversion. We will begin with the record type
representation.
First, we declare a record type named BITS with three fields of extremely limited range since we
only wish to store one or two bits in each field. Because of the limited range, we would like to
instruct the compiler to store the individual variables in very small memory units, and in addition, we
would like it to store all three fields in a single word. We do this in lines 13 through 17 where we
give the compiler the desired pattern for the three fields. It looks very much like a record except for
the substitution of the reserved words for and use in place of type and is in the record type definition.
Remember that we are modifying the type we have already declared to tell the compiler how to
actually implement it.
Each of the fields is slightly different also since the reserved word at is used followed by the number
0 in all three cases. This is telling the system to store this variable in the word with an offset of zero
from the first word, or in other words, we are telling the compiler to put this variable in the first word
of the record. Also, after the reserved word range, we have another defined range which tells the
compiler which bits of the word to store these in. The variable named Lower is therefore to be stored
in the first word, and is to occupy bit positions 0 and 1 of that word. The variable named Middle is
also to be stored in the first word, and will occupy bit position 2 of that word. The variable named
High will occupy bit positions 3 and 4 of the same word.
WHAT DO THE BIT POSITIONS MEAN?
By this point, you are probably wondering what is bit position 0. Is it the least significant bit or the
most significant bit? That question is entirely up to the compiler writer and you must consult your
documentation for the answer to this question and nearly any other questions you may have about this
new construct. One possible resulting bit pattern is illustrated in figure 32-1. The actual bit pattern for
your compiler may be something entirely different.

The INTEGER type variable consists of a 32 bit number on most microcomputers and nearly all
minicomputers, but even this is up to the implementor to define in any way he desires. Because 32
bits is fairly standard for the INTEGER type variable, and for a single word, the various fields of the
record were declared to be in one word to illustrate the next low level programming construct in Ada.
If your compiler is especially smart, you could continue the packing by telling the compiler to
squeeze the entire record into as few as 5 bits, since that is all that would be needed to actually store
the data. This would be done using the SIZE representation clause in a manner similar to the last
example program.
THERE IS A LIMIT TO THE MODIFICATIONS
After declaring the type, then modifying it to suit our purposes, we declare a variable of the new type
in line 19, which freezes the type and prevents any further type modifications. Note that it would be
an error to attempt to further modify the type after we have declared a variable of that type. This is
because it would allow declaring another variable of the newly modified type which would in fact be
different from the type of the first variable.
If additional fields were added with at 1 in their representation clauses, they would be put in the word
that was at an offset of 1 from the beginning of the record. This would be the second word of course.
You can see that it is possible to very carefully control where and how the data is stored in a record of
this data type.
A PROBLEM GENERATOR, USE WITH CAUTION
In line 22, we instantiate a copy of the generic function named Unchecked_Conversion to illustrate
its use. This is a function that can really get you into trouble, but can be a real time saver if you need
its capability. In this case, Switch_To_Bits will use an INTEGER for its source and a record of type
BITS as the result or target. A call to the function with an INTEGER type variable as an argument
will change the type of the variable and return the same value with a new type. In this case, because
the individual bits are packed into a single word, the data in the INTEGER type variable is actually
split up into the three fields of the record. The original data, as well as the three fields, are displayed
for a small range of values. In this case the composite data in the integer variable is unpacked into the
respective fields by the system.
Note that line 34 could have used the loop index named Index as the actual parameter since it is legal
in Ada to use a universal_integer in the call.
The only real requirement for use of the unchecked type conversion is that both structures have the
same number of program units or bits. The C programmer will recognize this as the union, and the
Pascal programmer should see that this is the same as using a variant record for type conversion.
Be sure to compile and run this program to see if it really does what it should. Your compiler may not
implement some or all of these features, in which case you can only study the result of execution
given at the end of the example program. We said at the beginning of this chapter that there would be
a few things you may not be able to do. Only two of the five compilers tested compiled this program
completely, and only one stored the bits in the pattern depicted in figure 32-1. Note that the
Unchecked_Conversion is not optional but required, and all five compilers tested by the author
compiled it properly.
THE PRAGMA NAMED PACK
Example program ------> e_c32_p3.ada
Examine the program named e_c32_p3.ada for an example of use of the pragma named PACK. This
is an instruction to the compiler to pack the data as tightly as possible with no concern for how long it
will take for the resulting program to execute. Three examples of packing are given here, with each
resulting in a more tightly packed composite type.
NORMAL PACKING DENSITY
Line 7 contains a declaration of a type which only requires 6 bits to store, but will probably use a full
word of 32 bits on most implementations. Lines 9 through 12 declare a record that may require 4
words because of alignment requirements in some compilers, and line 14 may even waste a few more
words due to alignment considerations. Lines 40 through 50 are used to output the sizes of these three
types for study. The results are given for two Ada compilers used with MS-DOS, running on an IBM-
PC type microcomputer.
SOME PACKING TAKES PLACE
In line 16 the same type is repeated with a different name, and is used in the record in lines 18
through 22 where it is packed using the pragma PACK. Note that the packing only takes place at the
record level, not at the lower level which is assumed to be the default packing density. In lines 24 and
25, the same array is declared with the pragma PACK used again at this level to achieve a better
packing density than the previous example. The results of execution illustrate that compiler 2 did a
little better at packing than compiler 1 did.
HIGHER PACKING DENSITY
Lines 27 through 37 illustrate an even higher packing density because of the representation clause in
line 28 where we instruct the compiler to use only 8 bits for the type used as elements in the
composite types. In this case, neither compiler supported the representation clause, so line 28 had to
be commented out resulting in no additional packing density.
WHICH COMPILER IS BEST?
Simply because one compiler did a better packing job does not make it best between the two
compared. There is a penalty to be paid here when it comes to executing the code because the data
fields are not located in exactly the same places for "normal" or unpacked data fields. The compiler
that packed the code very efficiently may take considerably longer to execute a program with data
stored in this way than the other. The important thing to remember is that the two compilers, even
though both are validated, handled the types slightly differently.
Keep in mind that the pragma named PACK only packs the data at the level at which it is
mentioned. It does not pack the data at lower levels unless it is mentioned there also. Three of the five
compilers were able to compile this program completely and correctly, except for line 28.
This is one example program that you should definitely compile and execute and do not depend on
the results of execution. Your output could be significantly different than either of the two results
illustrated.
THE ENUMERATED TYPE REPRESENTATION
Example program ------> e_c32_p4.ada
The example program named e_c32_p4.ada illustrates the use of the representation clause used with
the enumerated type variable to define the values of the enumerated values.
In this case we have declared a type named MOTION for use with some kind of a robot in which we
wish to instruct the robot to move any one of four directions or stop. A zero indicates a stopped
condition, and the four directions are actually four different bits of a binary code. Assuming that there
is a different relay or electronic switch for each direction, we can output a single field to control the
four relays or switches. The important thing is that the enumerated value is the pattern we wish to
output, so it can be output directly.
The enumerated type will work in exactly the same manner as any other enumerated type. You can
take the PRED, SUCC, VAL, or POS, and they will work the same way as if they were declared in
order. We have only changed the underlying representation of the enumerated type. The values must
be declared in ascending order or a compile error will be issued.
Be sure to compile and execute this program to ascertain that it is acceptable to your compiler. The
enumerated representation clause was available with three of the five compilers tested.
THE ADDRESS SPECIFICATION
The address specification is used in such a nebulous way that it is very difficult to even illustrate its
use in a general purpose program, so a program is not provided. The general form of its use is given
in the next two lines which represents a fragment of a program.
Robot_Port : MOTION; -- The port to control
-- direction
for Robot_Port use at 16#0175#; -- Absolute address of
-- the robot hardware
The first line of this sequence declares a variable named Robot_Port to be of type MOTION which
was declared in the example program named e_c32_p4.ada. You will recall that this type was
intended to be used to control the robot's direction. The second line tells the Ada compiler that this
variable must be assigned the absolute address of 0175 (hexadecimal), because this is where the
output port is located which controls the robot's direction. The reserved words for and use at tell the
compiler where to locate this particular variable.
It should now be obvious why it is not practical to write a general purpose program to illustrate this
concept. The location of a usable port will be different on every computer, and the means of
addressing the entire memory space can be quite complex in the case of segmented memory or with
some sort of memory management scheme. Consult the documentation that came with your compiler
to find the method used with your compiler to address an absolute memory location. It will be listed
in Annex M of your documentation, as required by the ARM.
UNCHECKED_DEALLOCATION
This is also a very low level routine that is mentioned here for completeness. Its use has been
illustrated previously in this tutorial in chapters 13 and 25, where it was used to free up space that had
been dynamically allocated.
PROGRAMMING EXERCISES
1. Write a program using Unchecked_Conversion to convert an INTEGER type variable into a
CHARACTER array of as long as needed to represent an INTEGER on your system,
probably two or four elements. Use the ORD attribute to convert the CHARACTER type
data to numerical values and display the numerical value of the components on the monitor.
This will identify the underlying representation of the INTEGER and the CHARACTER
types.(Solution)
2. Repeat exercise 1 to convert the FLOAT type to an array of CHARACTER.(Solution)
Advance to Chapter 33
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.
Ada Tutorial - Chapter 33
MORE EXAMPLE PROGRAMS
RANDOM NUMBERS
Example program ------> e_c33_p1.ada
Occasionally when writing a computer program, you will have a need to use a random number
generator. Very few compilers have a random number generator as a part of the system, so it is
necessary for you to either write one yourself or find one that has been written and debugged by
someone else. The example package named e_c33_p1.ada is presented to you for several reasons.
The first reason is to provide you with a useful example of a complete generic package to serve as an
illustration of how to write one. Secondly, it is a useful package that you can use as a part of your
own programs when you have a need to use a random number generator. Finally, it is an illustration
of good formatting style and it illustrates the inclusion of enough comments in the package
specification to completely define the method used, and how to utilize this package as part of another
program.
When Ada 83 was upgraded to Ada 95, a random number generator was defined as part of the
standard library making it a required component of every Ada 95 compilation system. The random
number generator in e_c33_p1.ada was not removed from the tutorial because it is a good example of
a well commented Ada utility package. No attempt has been made to prove that it generates truly
random numbers, so no claim can be made for it. It would be of benefit to you to study it, then use the
random number generator supplied with your compiler for any production work you do,
No other comments need to be given about the operation of this package, so you will be left on your
own to study the listing then compile this package in preparation for use with the next example
program.
TESTING THE RANDOM NUMBERS
Example program ------> e_c33_p2.ada
Examine the program named e_c33_p2.ada which was written solely to test the random number
generator in the package named Random. It instantiates a copy of the generic package using the type
FLOAT in line 7, then declares a few objects and an array type. In the executable part of the program
the random number generator is initialized with the Set_Seed procedure in line 21, and 12 random
numbers are read and printed to see that they do cover the range of 0.0 to 1.0 as defined in the header
of the package named Random.
The real test of the random number generator is in the loop beginning in line 35 where ten thousand
random numbers are generated and converted into integer type values by multiplying by 100. The
integer values will therefore cover a range of 1 to 100, and they are counted in the array named
Events. The count in each element of the array should be about 100 since there are ten thousand
cases distributed over 100 elements. Execution of the program will reveal that the count in each array
element is about 100 as expected, so we declare the random number generator to be at least
reasonably random. A mathematician may decide that this method is too crude to be called a good
random number generator, but for our purposes it is good enough.
Compile and execute this program, and you will find that each time you run it, you should get
different results because it uses the system clock to set the seed, resulting in a new starting seed for
each execution.
A NEW DYNAMIC STRING PACKAGE
Example program ------> e_c33_p3.ada
Examine the program named e_c16_p3.ada, which is included in part 2 of this tutorial for a better
dynamic string package. You will recall that when we studied the dynamic string package in chapter
16 of part 1 of this tutorial, we found a problem when using string constants in the Copy procedure
calls. This was because the system found ambiguous procedures. It could not tell if the string constant
was of type STRING or of our own declared type which we named DYNAMIC_STRING. Since we
had not studied the discriminated record at that time, we could not properly fix the problem. The new
DynStrng package, using a discriminated record, is offered as a better package for the problem of
using dynamic strings.
The DYNAMIC_STRING type is declared in lines 33 through 37 and is declared as a record this
time so there is no confusion as to whether it is a string or a record, and the overloading ambiguity
problem is gone. The package specification is essentially unchanged from the last dynamic string
package, except for the type of course, but the body is changed considerably to reflect the new data
structure. You will be left on your own to compare the bodies of these two packages if you so desire.
THE STRING CONSTANT PROBLEM IS FIXED
Example program ------> e_c33_p4.ada
The program named e_c33_p4.ada is designed to test the new package with a few string constants to
prove that it really does work as advertised. You can compile and execute this file to see that it really
does work with string constants in a Copy procedure call.
Example program ------> e_c16_p4.ada
You can return to the program named e_c16_p4.ada from chapter 16 to prove that the new package
still works with this old program. You will find that a couple of changes must be made to reflect the
different data type. Lines 11 and 12 must be modified to reflect only the upper limit on the static
length of the dynamic string variables. They will read as follows;
Name : DYNAMIC_STRING(15);
Stuff : DYNAMIC_STRING(35);
In addition, because the type is changed, lines 21 and 22 must also be modified as follows;
Name.Dynamic_Length := 3;
Stuff.Dynamic_Length := 7;
After making these two changes, this program should execute exactly as it did when it used the old
dynamic string package.
HOW OLD ARE YOU IN DAYS
Example program ------> e_c33_p5.ada
This program is a repeat of the program given in chapter 16, but it is improved somewhat here. Since
we now know how to use the Calendar package, we can use it to get today's date for us, and we do
this in the new program named e_c33_p5.ada. Notice especially the way the data is read in and
checked for validity before continuing on. If the data were read into the corresponding variables, an
invalid entry would cause an exception, but since the data is read into an INTEGER type variable
with a wide range, it can be checked for validity before being assigned to the correct variable with a
much smaller range. The program should be very simple for you to understand, but it would be good
for you to spend a little time studying it before compiling and executing it.
THE DINING PHILOSOPHERS
Example program ------> e_c33_p6.ada
Most books and articles on tasking or concurrency at least mention the problem of the dining
philosophers, so it would not be good to leave this tutorial without a little discussion of this problem.
In fact, the program named e_c33_p6.ada is a program you can study and execute to see this problem
illustrated.
The problem is stated that five philosophers sit down to eat. They like to eat for awhile then think for
awhile and repeat the pattern forever. In order to eat, they require a fork in both their left and their
right hands, and the table is set with a fork on each side of their plates. The problem occurs when we
state that there is only one fork between each adjacent philosopher, and he is therefore required to
share each fork with his adjacent colleague.
Each philosopher sits down, waits a random length of time, then picks up the fork on his left, waits
another random length of time, and picks up the fork on his right. He then proceeds to eat for a
random length of time, then returns both forks to the table and thinks for a random length of time.
Once he picks up the left fork, he stubbornly hangs on to it until he gets the right fork. If we ever
reach the condition where each philosopher has his left fork, then none will ever return it and none
can ever therefore pick up his right fork. The entire system is said to be deadlocked because nothing
else will ever be accomplished. All five of the uncooperative philosophers will eventually starve
since none can eat.
THE PROGRAM ILLUSTRATES DEADLOCK
The program uses a package to define a task type for one philosopher with the required delays and
the logic to acquire each fork, eat, then return the forks to the table. Following our study of tasking,
you should have no problem understanding the logic presented here.
The main program named Philos, which starts in line 93 of the file simply declares the five
philosophers, starts them through their cycles in lines 108 through 112, then loops while it is
watching for deadlock. When deadlock is detected, a message is output to the monitor, and the entire
system is aborted.
Compile and execute this program, so you can observe deadlock occurring and the system aborting
operation. If you run it several times, you will see that quite often deadlock occurs immediately, but
at other times it will run for several seconds before deadlock is detected.
This is an interesting problem, but the more interesting point is the fact that this program, which
begins in line 93 uses Ada.Text_IO and One_Man, and One_Man in turn uses Ada.Text_IO, Ada.
Calendar, and Random. This program uses quite a bit of the resources available in Ada, and uses
several packages to accomplish its intended mission. Since we are using Random, which was
developed for an earlier project, we are actually illustrating the reusability of Ada software.
THE GENERIC STACK
Example program ------> e_c33_p7.ada
We promised we would include a generic stack when we studied the character stack in chapter 16,
and e_c33_p7.ada is a generic stack to fulfill that promise. It is really only a copy of e_c16_p1.ada
from chapter 16 made into a generic package according to the rules studied in this tutorial.
Example program ------> e_c33_p8.ada
The program e_c16_p2.ada is nearly identical to the program of the same name from chapter 16, the
only difference being the instantiation of the generic package so it can be used in the main program.
Return to the Table of Contents
Copyright 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - [email protected] - Please email any comments or suggestions.

Programming in C, C++, Pascal or Ada
Time tested and complete, each programming tutorial is designed to teach you how to
program in C, C++, Pascal, Ada-83, or Ada-95. Our material includes programming
concepts, diagrams, source code for all example programs, and answers to all exercises.
An effective language instruction series appropriate for the beginner to intermediate
skillset.
Which language should I learn first?

Why should I study Coronado Enterprises tutorials?
Authored by a practicing engineer, Gordon Dodrill, each tutorial series has received
acclaim from countless satisfied users. Coronado Enterprises tutorials are distributed to
programmers throughout the world and have been proven effective since 1985.
Our commitment is to supply you with information that will lead you to success in your
programming endeavors. Try our Evaluation Package to find out if our writing style works
well for you.
Success to you,
Leora Dodrill and Gordon Dodrill
Coronado Enterprises

Evaluation Package
Examine any tutorial by viewing the text of the first three chapters online, or by
downloading ZIP Evaluation Package.
The benefit of downloading the Evaluation Package in ZIP format is the inclusion of
sample programs. You'll also receive practice exercises. If you choose to read the
chapters online, keep in mind this method does not include sample programs, however,
you will receive all source code when you purchase the complete programming tutorial.
Evaluation package includes:
- Introduction, Chapters 1, 2, and 3
- Concepts and discussion
- Sample programs and source code
- Experience through practical application. Learn by working through practice exercises.
The recommended method of study is to print the text for one or two chapters and study
the material by loading the example programs in the compiler's editor for viewing.
Following successful completion of each chapter, practice exercises provide additional
practice, exposure and experience.
Download and Evaluate Programming Tutorial


Full Programming Tutorial
Search this site



Programming Tutorial
Ada-83
Ada-95
C
C++
Pascal
Java

Programming Concepts
These topics are all covered in the
tutorials:

Memory Diagram
Arrays and Pointers
Strings
Pointers and Dynamic Allocation
Parameter Passing


Free evaluation
Examine the first three chapters of
a programming language series to
ensure our tutorials are:

Written in an informative style

Written in a format that will help
you master the concepts

Easy to understand

Valuable as supplemental
coursework material or stand
alone self study material


Instant download ...
Once you've had the opportunity to trial a programming tutorial, you may continue your
studies by purchasing the full version online. Each tutorial contains 12 or more chapters
and includes all sample programs. Also included are practice exercises and all source
code.
Tutorials are available for purchase, US $15.00 each. Once payment has been accepted
for your order, you will receive immediate download instructions.
Write to [email protected] for more information, or purchase
programming tutorials online.

Vinit Carpenter's website reviews C/C++ programming learning tools and recommends our C or C+
+ programming tutorial:
"This is one of the most complete tutorials out there. Once again,
most of the items covered here are apply across all platforms.
There are some items discussed here that are DOS dependent.
The tutorial includes a manual that covers all the aspects of the
C language. The archive also includes a huge collection of C
code that is discussed in this tutorial. The best way to learn
anything is by practical application and this tutorial does just
that."

Ada-83 | Ada-95 | C | C++ | Pascal

We accept payment through our
secure order form. Download
information quickly provided via
email.
Tutorials are available for
immediate use.



Other Info
Link to us
Code Board







{home} {about} {contact} {recommend}
{download} {purchase} {link} {code board}


Copyright 1985-2006 Coronado Enterprises.
Programming tutorials: C, C++, Pascal, and Ada
All rights reserved worldwide. Powered by WebStrata.net



Search SWCP Web Site


Search Knowledge Base





Web email [TWIG]

Web email [Horde]

Change password

Anti-spam filter

What's my IP address?

High Speed Connections

Web publishing

Traveling with iPass

Signup Online

Security Tips

Date: 10/11/2007
October Portal Is Online
The October issue of the SWCP Portal Newsletter is available
from http://www.swcp.com/swcp/newsletters/
This month's topics include: Beware domain name scammers;
Wireless networking perils; Lower prices for SWCP Web
Hosting; Tips to reduce certain kinds of spam; More FREE
internet classes in October.
Date: 9/28/2007
More Intro to the Internet Classes
On October 11th and 25th from 6pm - 8pm, and October 18th
from 2pm - 4pm SWCP will be offering more Intro to the
Internet classes, free to current SWCP customers. Limited space
available, so please call and reserve a spot if you're interested.


News from BBC technology stories
BBC News | Technology | World Edition
G Uruguay buys first $100 laptops
G 3 launches new Skype mobile phone
G MySpace casts unknown film stars
G Africa waiting for net revolution
G Apple ready to set Leopard free
G Schools warned off Microsoft deal
G Joystick win for Gears of War
G New room added to space station
Search
Search
G Virtual worlds threaten 'values'
G A380 superjumbo lands in Sydney
G Microsoft buys stake in Facebook
G Anti file-sharing laws considered
G BBC Top Gear to appear on PS3
G Wii helps Nintendo double profits
G Child abuse websites 'worsening'
G Carphone enters Vodafone tie-up
G Kicking off the free speech debate
G Share and share alike
G Why popstars are going it alone
G A wide world of games
G The game of love
G Man versus machine
G When work becomes a game
G Why is Facebook worth $15bn?
G Net giants test web health
G Overcoming your virtual fears
G Line up for spectrum gold rush
G Machinima to go mainstream
G Industry reacts to games review
G Hot picks: Machinima
* Historical message of the day can be found here
[About SWCP] [Businesses] [Members Services] [Products] [Account Info] [Links]

Southwest Cyberport
5021 Indian School NE Suite 600, Albuquerque NM 87110 USA
helpdesk / info request: [email protected] | webmaster: webmaster@swcp.
com
phone: USA: (505) 232-7992 | fax: USA: (505) 232-7975
Google Search
with Ada.Text_IO ; use Ada.Text_IO;procedure UglyForm
is begin Put ("Good form ") ;Put("can aid in ")
;Put ("understanding a program,");New_Line;Put
("and bad form ");Put ("can make a program ");Put("unreadable.");
New_Line;end UglyForm;
-- Result of execution
-- Good form can aid in understanding a program,
-- and bad form can make a program unreadable.
-- Chapter 2 - Programming Exercise 1
with Ada.Text_IO;
use Ada.Text_IO;
procedure Ch02_1 is
begin
Put("John Q. Doe");
end Ch02_1;
-- Result of execution
-- John Q. Doe
-- Chapter 2 - Programming Exercise 2
with Ada.Text_IO;
use Ada.Text_IO;
procedure Ch02_2 is
begin
Put("John Q. Doe");
New_Line;
Put("Anywhere, Anystate, USA, 12345");
New_Line;
Put("(123) 456-7890");
New_Line;
end Ch02_2;
-- Result of execution
-- John Q. Doe
-- Anywhere, Anystate, USA, 12345
-- (123) 456-7890
-- Chapter 3 - Program 1
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure OneInt is
Index : INTEGER; -- A simple Integer type
begin
Index := 23;
Put("The value of Index is");
Put(Index); -- The default field width is 11 columns
New_Line;
Index := Index + 12;
Put("The value of Index is");
Put(Index, 8);
New_Line;
end OneInt;
-- Result of execution
-- The value of Index is 23
-- The value of Index is 35
-- Chapter 3 - Program 2
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure MoreInts is
Index_1 : INTEGER;
Index_2, Index_3, Index_4 : INTEGER;
Cat : INTEGER := 12;
Dog : INTEGER := -5;
Pig, Hog, Sow : INTEGER := 1000;
begin
Index_1 := Cat + 4; -- Index_1 is 16
Index_2 := Dog - 3; -- Index_2 is -8
Index_3 := Pig * 7; -- Index_3 is 7000
Index_4 := Pig / 300; -- Index_4 is 3
Put("Index_1 = "); Put(Index_1); New_Line;
Put("Index_2 = "); Put(Index_2); New_Line;
Put("Index_3 = "); Put(Index_3); New_Line;
Put("Index_4 = "); Put(Index_4); New_Line(2);
Index_1 := 5 * Cat - Pig / 4 ; -- Index_1 is -190
Index_2 := (5 * Cat) - (Pig / 4); -- Index_2 is -190
Index_3 := Pig mod 3; -- Index_3 is 1
Index_4 := Pig rem 3; -- Index_4 is 1
Put("Index_1 = "); Put(Index_1); New_Line;
Put("Index_2 = "); Put(Index_2); New_Line;
Put("Index_3 = "); Put(Index_3); New_Line;
Put("Index_4 = "); Put(Index_4); New_Line(2);
Index_1 := abs(Dog); -- Index_1 is 5
Index_2 := Cat**3; -- Index_2 is 1728
Index_3 := (Cat-5)**(abs(Dog)-2); -- Index_3 is 343
Index_4 := -Index_3; -- Index_4 is -343
Put("Index_1 = "); Put(Index_1); New_Line;
Put("Index_2 = "); Put(Index_2); New_Line;
Put("Index_3 = "); Put(Index_3); New_Line;
Put("Index_4 = "); Put(Index_4); New_Line(2);
end MoreInts;
-- Result of execution
-- Index_1 = 16
-- Index_2 = -8
-- Index_3 = 7000
-- Index_4 = 3
--
-- Index_1 = -190
-- Index_2 = -190
-- Index_3 = 1
-- Index_4 = 1
--
-- Index_1 = 5
-- Index_2 = 1728
-- Index_3 = 343
-- Index_4 = -343
-- Chapter 3 - Program 3
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure InitEx is
Index_1, Index_2, Index_3 : INTEGER;
This_Is_A_Long_Variable_Name : INTEGER;
DOZEN : constant INTEGER := 12;
GROSS : constant INTEGER := 12 * DOZEN;
BIG_NO : constant := 32_24_7; -- This is 32247
TWO : constant := BIG_NO - 3_22_45; -- This is 2
begin
Index_1 := GROSS; -- Index_1 is 144
Index_2 := BIG_NO - TWO; -- Index_2 is 32245
Index_3 := TWO * GROSS; -- Index_3 is 288
This_Is_A_Long_Variable_Name := DOZEN * DOZEN - GROSS;
Put("Index_1 = "); Put(Index_1); New_Line;
Put("Index_2 = "); Put(Index_2); New_Line;
Put("Index_3 = "); Put(Index_3); New_Line;
Put("This_Is_A_Long_Variable_Name =");
Put(This_Is_A_Long_Variable_Name); New_Line;
Index_1 := 123E2; -- 12300
Index_2 := 1_23e2; -- 12300
Index_3 := 12_3e+2; -- 12300
Index_1 := 2#10111#; -- Binary number
Index_2 := 8#377#; -- Octal number
Index_3 := 16#1FF#e1; -- Hexadecimal number
Index_1 := 12#A4#; -- Base 12 number
end InitEx;
-- Result of execution
-- Index_1 = 144
-- Index_2 = 32245
-- Index_3 = 288
-- This_Is_A_Long_Variable_Name = 0
-- Chapter 3 - Program 4
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure SubTypes is
type MY_INT_TYPE is range -10_000..20_000;
My_Int : MY_INT_TYPE;
package My_Int_IO is new Ada.Text_IO.Integer_IO(MY_INT_TYPE);
use My_Int_IO;
subtype SUB_INT is INTEGER range 12..144;
Thing : SUB_INT;
Count : INTEGER;
Stuff : INTEGER range 12..144;
START : constant := 4; -- START is 4
STOP : constant := START + 13; -- STOP is 17
Example : SUB_INT range 4*START..2*STOP + 7; -- 16..41
Example2 : MY_INT_TYPE range START..2*STOP + 7; -- 4..41
begin
Thing := 6 * 3; -- Thing is 18
Count := Thing + 17; -- Count is 35
Stuff := Count - Thing; -- Stuff is 17
My_Int := 6 * 3; -- My_Int is 18
My_Int := MY_INT_TYPE(Thing + 17); -- My_Int is 35
My_Int := MY_INT_TYPE(Thing) + 17; -- My_Int is 35
My_Int := MY_INT_TYPE(Thing) - 10; -- My_Int is 8
My_Int := MY_INT_TYPE(Thing - 10); -- Run Time Error
My_Int := 35;
Thing := SUB_INT(My_Int - 17); -- Thing is 18
Thing := SUB_INT(My_Int - 27); -- Run Time Error
Example := Thing + SUB_INT(My_Int);
end SubTypes;
-- Result of execution
-- Exception never handled: constraint_error
-- Value 8 out of range 12..144.
-- Chapter 3 - Program 5
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure IntAttrs is
type BUG_RANGE is range -13..34;
Rat : INTEGER;
Dog : NATURAL;
Cat : POSITIVE;
Bug : BUG_RANGE;
begin
Rat := 12;
Dog := 23;
Cat := 31;
Bug := -11;
Put("The type INTEGER uses ");
Put(INTEGER'SIZE);
Put(" bits of memory,");
New_Line;
Put(" and has a range from ");
Put(INTEGER'FIRST);
Put(" to ");
Put(INTEGER'LAST);
New_Line;
Put(" Rat has a present value of ");
Put(Rat);
New_Line(2);
Put("The type NATURAL uses ");
Put(NATURAL'SIZE);
Put(" bits of memory,");
New_Line;
Put(" and has a range from ");
Rat := NATURAL'FIRST;
Put(Rat);
Put(" to ");
Rat := NATURAL'LAST;
Put(Rat);
New_Line;
Put(" Dog has a present value of ");
Put(Dog);
New_Line(2);
Put("The type POSITIVE uses ");
Put(POSITIVE'SIZE);
Put(" bits of memory,");
New_Line;
Put(" and has a range from ");
Put(POSITIVE'FIRST);
Put(" to ");
Put(POSITIVE'LAST);
New_Line;
Put(" Cat has a present value of ");
Put(Cat);
New_Line(2);
Put("The type BUG_RANGE uses ");
Put(INTEGER(BUG_RANGE'SIZE));
Put(" bits of memory,");
New_Line;
Put(" and has a range from ");
Put(INTEGER(BUG_RANGE'FIRST));
Put(" to ");
Put(INTEGER(BUG_RANGE'LAST));
New_Line;
Put(" Bug has a present value of ");
Put(INTEGER(Bug));
New_Line(2);
end IntAttrs;
-- Result of execution
-- The type INTEGER uses 32 bits of memory,
-- and has a range from -2147483648 to 2147483647
-- Rat has a present value of 12
-- The type NATURAL uses 31 bits of memory,
-- and has a range from 0 to 2147483647
-- Dog has a present value of 23
-- The type POSITIVE uses 31 bits of memory,
-- and has a range from 1 to 2147483647
-- Cat has a present value of 31
-- The type BUG_RANGE uses 7 bits of memory,
-- and has a range from -13 to 34
-- Bug has a present value of -11
-- Chapter 3 - Programming Exercise 1
procedure Ch03_1 is
subtype RESTRICTED_RANGE is INTEGER range 12.. 77;
Restricted_Variable : RESTRICTED_RANGE;
begin
Restricted_Variable := 5;
Restricted_Variable := 125;
Restricted_Variable := 15 * (15 - 5);
end Ch03_1;
-- Result of Execution
-- (You will get an error for the executable statements, fix
-- each one and observe the error at the next step.
-- Chapter 3 - Programming Exercise 2
procedure Ch03_2 is
Index, Count : INTEGER;
type NEW_INT is range 0..1200;
New_Index, New_Count : NEW_INT;
begin
Index := 100;
New_Index := 100;
Index := New_Index;
Count := Index + New_Index;
New_Count := Index + New_Index;
end Ch03_2;
-- Result of execution
-- (This has compilation errors in lines 12, 13, and 14.)
-- Chapter 3 - Programming Exercise 3
-- Chapter 3 - Program 5
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Ch03_3 is
type BUG_RANGE is range -13..34;
package Bug_IO is new Ada.Text_IO.Integer_IO(BUG_RANGE);
use Bug_IO;
Rat : INTEGER;
Dog : NATURAL;
Cat : POSITIVE;
Bug : BUG_RANGE;
begin
Rat := 12;
Dog := 23;
Cat := 31;
Bug := -11;
Put("The type INTEGER uses ");
Ada.Integer_Text_IO.Put(INTEGER'SIZE);
Put(" bits of memory,");
New_Line;
Put(" and has a range from ");
Put(INTEGER'FIRST);
Put(" to ");
Put(INTEGER'LAST);
New_Line;
Put(" Rat has a present value of ");
Put(Rat);
New_Line(2);
Put("The type NATURAL uses ");
Ada.Integer_Text_IO.Put(NATURAL'SIZE);
Put(" bits of memory,");
New_Line;
Put(" and has a range from ");
Rat := NATURAL'FIRST;
Put(Rat);
Put(" to ");
Rat := NATURAL'LAST;
Put(Rat);
New_Line;
Put(" Dog has a present value of ");
Put(Dog);
New_Line(2);
Put("The type POSITIVE uses ");
Ada.Integer_Text_IO.Put(POSITIVE'SIZE);
Put(" bits of memory,");
New_Line;
Put(" and has a range from ");
Put(POSITIVE'FIRST);
Put(" to ");
Put(POSITIVE'LAST);
New_Line;
Put(" Cat has a present value of ");
Put(Cat);
New_Line(2);
Put("The type BUG_RANGE uses ");
Bug_IO.Put(BUG_RANGE'SIZE);
Put(" bits of memory,");
New_Line;
Put(" and has a range from ");
Put(BUG_RANGE'FIRST);
Put(" to ");
Put(BUG_RANGE'LAST);
New_Line;
Put(" Bug has a present value of ");
Put(Bug);
New_Line(2);
end Ch03_3;
-- Result of execution
-- The type INTEGER uses 32 bits of memory
-- and has a range from -2147483648 to 2147483647
-- Rat has a present value of 12
-- The type NATURAL uses 31 bits of memory
-- and has a range from 0 to 2147483647
-- Dog has a present value of 23
-- The type POSITIVE uses 31 bits of memory
-- and has a range from 1 to 2147483647
-- Cat has a present value of 31
-- The type BUG_RANGE uses 7 bits of memory
-- and has a range from -13 to 34
-- Bug has a present value of -11
-- Chapter 4 - Program 1
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;
-- Result of execution
-- Which now has the value of TRUE
-- Which now has the value of FALSE
-- Chapter 4 - Program 2
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Compares is
package Enum_IO is new Ada.Text_IO.Enumeration_IO(BOOLEAN);
use Enum_IO;
Index, Count : INTEGER := 12;
Truth, Lies, Question : BOOLEAN;
begin
Truth := Index = Count; -- This is TRUE
Lies := Index < Index; -- This is FALSE
-- Examples of all BOOLEAN operators
Question := Index = Count; -- Equality
Question := Index /= Count; -- Inequality
Question := Index < Count; -- Less than
Question := Index <= Count; -- Less than or equal
Question := Index > Count; -- Greater than
Question := Index >= Count; -- Greater than or equal
-- Examples of composite BOOLEAN expressions
Question := Index = 12 and Count = 12 and Truth and TRUE;
Question := Index /= 12 or FALSE or Count > 3 or Truth;
Question := (Truth or Lies) and (Truth and not Lies);
Question := Truth xor Lies;
-- now for short circuit evaluation
Question := Index /= Count and then Index = 9/(Index - Count);
Question := Index = Count or else Index = 9/(Index - Count);
Question := (Index = Count) or else (Index = 9/(Index - Count));
end Compares;
-- Result of execution
-- (No output generated by this program.)
-- Chapter 4 - Programming exercise 1
-- Chapter 4 - Program 1
with Ada.Text_IO;
use Ada.Text_IO;
procedure Ch04_1 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;
Put("Is_It now has the value of ");
Put(Is_It);
New_Line;
Is_It := One /= Two;
Put("Is_It now has the value of ");
Put(Is_It);
New_Line;
Is_It := One + Two >= Three;
Put("Is_It now has the value of ");
Put(Is_It);
New_Line;
end Ch04_1;
-- Result of execution
-- Which now has the value of TRUE
-- Which now has the value of FALSE
-- Is_It now has the value of TRUE
-- Is_It now has the value of TRUE
-- Is_It now has the value of TRUE
-- Chapter 5 - Program 1
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure LoopDemo is
Index, Count : INTEGER;
begin
Index := 1;
loop -- This is the simplest loop
Put("Index =");
Put(Index, 5); New_Line;
Index := Index + 1;
exit when Index = 5;
end loop;
Index := 1;
loop -- Another simplest loop
Put("Index =");
Put(Index, 5); New_Line;
Index := Index + 1;
if Index = 5 then exit; end if;
end loop;
Count := 1;
while Count < 5 loop -- This is the while loop
Put("Count =");
Put(Count, 5); New_Line;
Count := Count + 1;
end loop;
for Index in 1..4 loop -- This is the for loop
Put("Doubled index =");
Put(2 * Index, 5); New_Line;
end loop;
for Count in reverse 5..8 loop -- This is the reverse for loop
Put("Triple count =");
Put(3 * Count, 5); New_Line;
end loop;
for Index in 7..11 loop -- An empty loop
null;
end loop;
end LoopDemo;
-- Result of execution
-- Index = 1
-- Index = 2
-- Index = 3
-- Index = 4
-- Index = 1
-- Index = 2
-- Index = 3
-- Index = 4
-- Count = 1
-- Count = 2
-- Count = 3
-- Count = 4
-- Doubled index = 2
-- Doubled index = 4
-- Doubled index = 6
-- Doubled index = 8
-- Triple count = 24
-- Triple count = 21
-- Triple count = 18
-- Triple count = 15
-- Chapter 5 - Program 2
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure MoreLoop is
type MY_TYPE is range 10..13;
package My_Int_IO is new Ada.Text_IO.Integer_IO(MY_TYPE);
use My_Int_IO;
My_Range : MY_TYPE;
TWO : constant INTEGER := 2;
THREE : constant INTEGER := 3;
FOUR : constant INTEGER := 4;
Height,Width : INTEGER;
Special_Index : INTEGER;
begin
for Index in MY_TYPE loop
Put("Going through the first loop");
Put(Index, 3);
New_Line;
end loop;
for Index in MY_TYPE'FIRST..MY_TYPE'LAST loop
Put("Going through the second loop");
Put(Index, 3);
New_Line;
end loop;
for Index in TWO..THREE**2 - FOUR loop -- range is 2..5
Put("Going through the third loop");
Put(Index, 3);
New_Line;
end loop;
Named_Loop:
for Height in TWO..FOUR loop
for Width in THREE..5 loop
if Height * Width = 12 then
exit Named_Loop;
end if;
Put("Now we are in the nested loop and area is");
Put(Height*Width, 5);
New_Line;
end loop;
end loop Named_Loop;
Special_Index := 157;
for Special_Index in 3..6 loop
Put("In the Special Index loop");
Put(Special_Index, 5);
New_Line;
end loop;
Put("The Special Index loop is completed");
Put(Special_Index, 5);
New_Line;
end MoreLoop;
-- Result of execution
-- Going through the first loop 10
-- Going through the first loop 11
-- Going through the first loop 12
-- Going through the first loop 13
-- Going through the second loop 10
-- Going through the second loop 11
-- Going through the second loop 12
-- Going through the second loop 13
-- Going through the third loop 2
-- Going through the third loop 3
-- Going through the third loop 4
-- Going through the third loop 5
-- Now we are in the nested loop and area is 6
-- Now we are in the nested loop and area is 8
-- Now we are in the nested loop and area is 10
-- Now we are in the nested loop and area is 9
-- In the Special Index loop 3
-- In the Special Index loop 4
-- In the Special Index loop 5
-- In the Special Index loop 6
-- The Special Index loop is completed 157
-- Chapter 5 - Program 3
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure IfDemo is
begin
for Index in 1..7 loop -- This contains two simple if statements
Put("Index is ");
Put(Index, 3);
if Index < 4 then
Put(" so is less than 4");
end if;
if Index > 5 then
Put(" so is more than 5");
end if;
New_Line;
end loop;
New_Line;
for Index in 13..17 loop -- This contains an else clause
Put("Index is");
Put(Index, 3);
if Index < 15 then
Put_Line(" and is less than 15.");
else
Put_Line(" and is 15 or greater.");
end if;
end loop;
New_Line;
for Index in 13..17 loop -- This introduces the elsif statement
Put("Index is");
Put(Index, 3);
if Index < 15 then
Put_Line(" and is less than 15.");
elsif Index = 15 then
Put_Line(" and is 15.");
elsif Index = 16 then
Put_Line(" and is 16.");
else
Put_Line(" and is greater than 16.");
end if;
end loop;
New_Line;
-- This final group of statements contains a loop with a nested if
-- statement, and a loop within the the else part of the nested
-- if statement.
for Index in 13..17 loop
Put("Index is");
Put(Index, 3);
if Index < 16 then
if Index > 14 then
Put(" and is less than 16 and greater than 14.");
else
Put(" and is less than or equal to 14.");
end if;
else
Put(" and is 16 or greater.");
for New_Index in 222..224 loop
Put(" stutter");
end loop;
end if;
New_Line;
end loop;
end IfDemo;
-- Result of execution
-- Index is 1 so is less than 4
-- Index is 2 so is less than 4
-- Index is 3 so is less than 4
-- Index is 4
-- Index is 5
-- Index is 6 so is more than 5
-- Index is 7 so is more than 5
--
-- Index is 13 and is less than 15.
-- Index is 14 and is less than 15.
-- Index is 15 and is 15 or greater.
-- Index is 16 and is 15 or greater.
-- Index is 17 and is 15 or greater.
--
-- Index is 13 and is less than 15.
-- Index is 14 and is less than 15.
-- Index is 15 and is 15.
-- Index is 16 and is 16.
-- Index is 17 and is greater than 16.
--
-- Index is 13 and is less than or equal to 14.
-- Index is 14 and is less than or equal to 14.
-- Index is 15 and is less than 16 and greater than 14.
-- Index is 16 and is 16 or greater. stutter stutter stutter
-- Index is 17 and is 16 or greater. stutter stutter stutter
-- Chapter 5 - Program 4
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure CaseDemo is
begin
for How_Many in 4..13 loop
Put("We now have ");
Put(How_Many, 3);
Put(" widgets, ");
case How_Many is
when 4..6 => Put("which is too few.");
when 7|9 => Put("but we don't need 7 or 9.");
when 13 => Put("but that is too many.");
when 8|10|12 => Put("which is a large even number.");
when 11 => Put("enough for a football team.");
end case;
New_Line;
end loop;
New_Line;
for How_Many in 100..105 loop
Put("It is now ");
Put(How_Many, 3);
Put(" ");
case How_Many is
when 100 => Put("The value is 100, and useless.");
when 101 => for Index in 2..5 loop
Put("Puppy ");
end loop;
when 103 => if TRUE then
Put("Of course TRUE will always be true.");
end if;
when 105 => null;
when others => Put("This is one of those not defined.");
end case;
New_Line;
end loop;
end CaseDemo;
-- Result of execution
-- We now have 4 widgets, which is too few.
-- We now have 5 widgets, which is too few.
-- We now have 6 widgets, which is too few.
-- We now have 7 widgets, but we don't need 7 or 9.
-- We now have 8 widgets, which is a large even number.
-- We now have 9 widgets, but we don't need 7 or 9.
-- We now have 10 widgets, which is a large even number.
-- We now have 11 widgets, enough for a football team.
-- We now have 12 widgets, which is a large even number.
-- We now have 13 widgets, but that is too many.
--
-- It is now 100 The value is 100, and useless.
-- It is now 101 Puppy Puppy Puppy Puppy
-- It is now 102 This is one of those not defined.
-- It is now 103 Of course TRUE will always be true.
-- It is now 104 This is one of those not defined.
-- It is now 105
-- Chapter 5 - Program 5
with Ada.Text_IO;
use Ada.Text_IO;
procedure GoToDemo is
begin
goto Some_Place;
<<There>>
Put_Line("I am now at the There place.");
goto Stop_This_Mess;
<<Where>>
Put_Line("I am now at the Where place.");
goto There;
<<Some_Place>>
Put_Line("I am now Some_Place.");
goto Where;
<<Stop_This_Mess>>
Put_Line("I am now about to end this mess.");
end GoToDemo;
-- Result of execution
-- I am now Some_Place.
-- I am now at the Where place.
-- I am now at the There place.
-- I am now about to end this mess.
-- Chapter 5 - Program 6
-- Centigrade to Farenheit temperature table
--
-- This program generates a list of Centigrade and Farenheit
-- temperatures with a note at the freezing point of water
-- and another note at the boiling point of water.
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure TempConv is
Centigrade, Farenheit : INTEGER;
begin
Put("Centigrade to Farenheit temperature table");
New_Line(2);
for Count in INTEGER range -2..12 loop
Centigrade := 10 * Count;
Farenheit := 32 + Centigrade * 9 / 5;
Put("C =");
Put(Centigrade, 5);
Put(" F =");
Put(Farenheit, 5);
if Centigrade = 0 then
Put(" Freezing point of water");
end if;
if Centigrade = 100 then
Put(" Boiling point of water");
end if;
New_Line;
end loop;
end TempConv;
-- Result of execution
-- Centigrade to Farenheit temperature table
--
-- C = -20 F = -4
-- C = -10 F = 14
-- C = 0 F = 32 Freezing point of water
-- C = 10 F = 50
-- C = 20 F = 68
-- C = 30 F = 86
-- C = 40 F = 104
-- C = 50 F = 122
-- C = 60 F = 140
-- C = 70 F = 158
-- C = 80 F = 176
-- C = 90 F = 194
-- C = 100 F = 212 Boiling point of water
-- C = 110 F = 230
-- C = 120 F = 248
-- Chapter 5 - Program 7
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure DumbConv is
X, Y : INTEGER;
begin
Put("Centigrade to Farenheight temperature table");
New_Line(2);
for Count in INTEGER range -2..12 loop
X := 10 * Count;
Y := 32 + X * 9 / 5;
Put("C =");
Put(X, 5);
Put(" F =");
Put(Y, 5);
if X = 0 then
Put(" Freezing point of water");
end if;
if X = 100 then
Put(" Boiling point of water");
end if;
New_Line;
end loop;
end;
-- Result of execution
-- Centigrade to Farenheit temperature table
--
-- C = -20 F = -4
-- C = -10 F = 14
-- C = 0 F = 32 Freezing point of water
-- C = 10 F = 50
-- C = 20 F = 68
-- C = 30 F = 86
-- C = 40 F = 104
-- C = 50 F = 122
-- C = 60 F = 140
-- C = 70 F = 158
-- C = 80 F = 176
-- C = 90 F = 194
-- C = 100 F = 212 Boiling point of water
-- C = 110 F = 230
-- C = 120 F = 248
-- Chapter 5 - Programming exercise 1
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Ch05_1 is
Year : INTEGER;
begin
for Age in 0..21 loop
Put("In");
Put(Age + 1938,5);
Put(", I was");
Put(Age,3);
Put(" years old");
if Age = 5 then
Put(", and started school");
end if;
if Age = 17 then
Put(", and graduated from high school");
end if;
Put(".");
New_Line;
end loop;
end Ch05_1;
-- Result of execution
-- In 1938, I was 0 years old.
-- In 1939, I was 1 years old.
-- In 1940, I was 2 years old.
-- In 1941, I was 3 years old.
-- In 1942, I was 4 years old.
-- In 1943, I was 5 years old, and started school.
-- In 1944, I was 6 years old.
-- In 1945, I was 7 years old.
-- In 1946, I was 8 years old.
-- In 1947, I was 9 years old.
-- In 1948, I was 10 years old.
-- In 1949, I was 11 years old.
-- In 1950, I was 12 years old.
-- In 1951, I was 13 years old.
-- In 1952, I was 14 years old.
-- In 1953, I was 15 years old.
-- In 1954, I was 16 years old.
-- In 1955, I was 17 years old, and graduated from high school.
-- In 1956, I was 18 years old.
-- In 1957, I was 19 years old.
-- In 1958, I was 10 years old.
-- In 1959, I was 21 years old.
-- Chapter 5 - Programming exercise 2
with Ada.Text_IO, ADa.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Ch05_2 is
Year : INTEGER;
begin
for Age in 0..21 loop
Put("In");
Put(Age + 1938,5);
Put(", I was");
Put(Age,3);
Put(" years old");
case Age is
when 5 => Put(", and started school");
when 17 => Put(", and graduated from high school");
when others => null;
end case;
Put(".");
New_Line;
end loop;
end Ch05_2;
-- Result of execution
-- In 1938, I was 0 years old.
-- In 1939, I was 1 years old.
-- In 1940, I was 2 years old.
-- In 1941, I was 3 years old.
-- In 1942, I was 4 years old.
-- In 1943, I was 5 years old, and started school.
-- In 1944, I was 6 years old.
-- In 1945, I was 7 years old.
-- In 1946, I was 8 years old.
-- In 1947, I was 9 years old.
-- In 1948, I was 10 years old.
-- In 1949, I was 11 years old.
-- In 1950, I was 12 years old.
-- In 1951, I was 13 years old.
-- In 1952, I was 14 years old.
-- In 1953, I was 15 years old.
-- In 1954, I was 16 years old.
-- In 1955, I was 17 years old, and graduated from high school.
-- In 1956, I was 18 years old.
-- In 1957, I was 19 years old.
-- In 1958, I was 10 years old.
-- In 1959, I was 21 years old.
-- Chapter 6 - Program 1
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure AllInt is
Data : INTEGER;
Form : POSITIVE;
Once : NATURAL;
type MY_INTEGER is range -1000..24000;
type MY_SHORT is range -12..127;
subtype MY_SUBTYPE is MY_INTEGER range -12..127;
Index : MY_INTEGER := 345;
Stuff : MY_INTEGER := 33;
Count : MY_SHORT := 54;
begin
Put("The type MY_SHORT covers the range of");
Data := INTEGER(MY_SHORT'FIRST);
Put(Data);
Put(" to");
Data := INTEGER(MY_SHORT'LAST);
Put(Data);
New_Line;
Put("and its base covers the range of");
Data := INTEGER(MY_SHORT'BASE'FIRST);
Put(Data);
Put(" to");
Data := INTEGER(MY_SHORT'BASE'LAST);
Put(Data);
New_Line(2);
Put("The type MY_INTEGER covers the range of");
Put(INTEGER(MY_INTEGER'FIRST));
Put(" to");
Put(INTEGER(MY_INTEGER'LAST));
New_Line;
Put("and its base covers the range of");
Put(INTEGER(MY_INTEGER'BASE'FIRST));
Put(" to");
Put(INTEGER(MY_INTEGER'BASE'LAST));
New_Line(2);
if Index in MY_SUBTYPE then
Put_Line("Index is in the range of MY_SUBTYPE");
end if;
if Index not in MY_SUBTYPE then
Put_Line("Index is not in the range of MY_SUBTYPE");
end if;
if Index in 12..377 then
Put_Line("Index is in the range of 12..377");
end if;
if Index not in Stuff..3 * (Stuff - 4) then
Put_Line("Index is not in the range of Stuff..3 * (Stuff - 4)");
end if;
end AllInt;
-- Result of execution
-- The type MY_SHORT covers the range of -12 to 127
-- and its base covers the range of -128 to 127
--
-- The type MY_INTEGER covers the range of -1000 to 24000
-- and its base covers the range of -32768 to 32767
--
-- Index is not in the range of MY_SUBTYPE
-- Index is in the range of 12..377
-- Index is not in the range of Stuff..3 * (Stuff - 4)
-- Chapter 6 - Program 2
with Ada.Text_IO;
use Ada.Text_IO;
procedure Enum is
type DAY is (MON, TUE, WED, THU, FRI, SAT, SUN);
subtype WORK_DAY is DAY range MON..FRI;
subtype PLAY_DAY is DAY range SAT..SUN;
type HEAVENLY_BODY is (MOON, SUN, EARTH, MARS);
Big_Sphere : HEAVENLY_BODY;
package Day_IO is new Ada.Text_IO.Enumeration_IO(DAY);
use Day_IO;
package Body_IO is new Ada.Text_IO.Enumeration_IO(HEAVENLY_BODY);
use Body_IO;
Day_Of_Week : DAY;
Today : DAY;
Happy_Day : PLAY_DAY;
Bowling_Day : DAY range THU..SAT;
Index : INTEGER;
begin
Day_Of_Week := WED; -- WED
Day_Of_Week := DAY'FIRST; -- MON
Day_Of_Week := DAY'LAST; -- SUN
Day_Of_Week := DAY'PRED(Day_Of_Week); -- SAT
Day_Of_Week := DAY'SUCC(PLAY_DAY'FIRST); -- SUN
Index := DAY'POS(MON); -- 0
Index := DAY'POS(WED); -- 2
Day_Of_Week := DAY'VAL(1); -- TUE
for Day_Of_Week in WORK_DAY loop
Put("We are in the workday loop");
New_Line;
end loop;
Today := THU;
if Today <= WED then
Put("Early in the week");
New_Line;
end if;
if Today >= WED then
Put("Late in the week");
New_Line;
end if;
Today := SUN;
Big_Sphere := SUN;
Today := DAY'(SUN);
Big_Sphere := HEAVENLY_BODY'(SUN);
Put(Today);
Put(DAY'PRED(Today));
Put_Line(" from type DAY.");
Put(Big_Sphere);
Put(HEAVENLY_BODY'PRED(Big_Sphere));
Put_Line(" from type HEAVENLY_BODY");
end Enum;
-- Result of execution
-- We are in the workday loop
-- We are in the workday loop
-- We are in the workday loop
-- We are in the workday loop
-- We are in the workday loop
-- Late in the week
-- SUNSAT from type DAY.
-- SUNMOON from type HEAVENLY_BODY
-- Chapter 6 - Program 3
with Ada.Text_IO;
use Ada.Text_IO;
procedure BoolVars is
package Bool_IO is new Ada.Text_IO.Enumeration_IO(BOOLEAN);
use Bool_IO;
Correct : BOOLEAN;
Maybe : BOOLEAN;
Probably : BOOLEAN;
begin
Correct := TRUE; -- TRUE
Maybe := FALSE; -- FALSE
Probably := Correct or Maybe; -- TRUE
Probably := Correct and Maybe; -- FALSE
Probably := Correct xor Maybe; -- TRUE
Probably := Correct and not Maybe; -- TRUE
Probably := BOOLEAN'FIRST; -- FALSE
Probably := BOOLEAN'LAST; -- TRUE
if Maybe < Correct then
Put("FALSE is of less value than TRUE in a BOOLEAN variable");
New_Line;
end if;
Put(Correct, 8);
Put(Maybe, 8);
New_Line;
end BoolVars;
-- Result of execution
-- FALSE is of less value than TRUE in a BOOLEAN variable
-- TRUE FALSE
-- Chapter 6 - Program 4
with Ada.Text_IO;
use Ada.Text_IO;
procedure IncrInt is
Index : INTEGER;
begin
Index := 13;
Index := INTEGER'POS(Index); -- Still 13
Index := INTEGER'VAL(Index); -- Still 13
Index := INTEGER'SUCC(Index); -- Incremented to 14
Index := INTEGER'PRED(Index); -- Decremented to 13
Index := Index + 1; -- preferred method of incrementing
end IncrInt;
-- Result of execution
-- (No output data from this program.)
-- Chapter 6 - Program 5
with Ada.Text_IO;
use Ada.Text_IO;
procedure Modular is
type DIAL_RANGE is mod 5;
Dial : DIAL_RANGE := 3;
type MY_BINARY_BIT is mod 2;
My_Bit : MY_BINARY_BIT := 1;
type MY_UNSIGNED_SHORT_INT is mod 65536;
type MY_UNSIGNED_BYTE is mod 256;
package Mod_IO is new Ada.Text_IO.Modular_IO(DIAL_RANGE);
use Mod_IO;
package Bit_IO is new Ada.Text_IO.Modular_IO(MY_BINARY_BIT);
use Bit_IO;
begin
for Index in 1..6 loop
Dial := Dial + 1;
Put("The value of Dial is ");
Put(Dial);
Put(", and the binary bit is ");
Put(My_Bit);
My_Bit := My_Bit + 1;
-- My_Bit := My_Bit + 2; -- Error, 2 is too big to add to My_Bit
New_Line;
end loop;
New_Line;
for Index in 1..6 loop
Dial := Dial - 1;
Put("The value of Dial is ");
Put(Dial);
Put(", and the binary bit is ");
Put(My_Bit);
My_Bit := My_Bit - 1;
New_Line;
end loop;
end Modular;
-- Result of execution
--
--- The value of Dial is 4, and the binary bit is 1
--- The value of Dial is 0, and the binary bit is 0
--- The value of Dial is 1, and the binary bit is 1
--- The value of Dial is 2, and the binary bit is 0
--- The value of Dial is 3, and the binary bit is 1
--- The value of Dial is 4, and the binary bit is 0
--- The value of Dial is 3, and the binary bit is 1
--- The value of Dial is 2, and the binary bit is 0
--- The value of Dial is 1, and the binary bit is 1
--- The value of Dial is 0, and the binary bit is 0
--- The value of Dial is 4, and the binary bit is 1
--- The value of Dial is 3, and the binary bit is 0
-- Chapter 6 - Program 7
with Ada.Text_IO;
use Ada.Text_IO;
procedure FloatVar is
PI : constant := 3.1416;
TWO_PI : constant := 2 * PI;
R : FLOAT;
type MY_FLOAT is digits 7;
type MY_LONG_FLOAT is digits 15;
Area : MY_FLOAT := 2.345_12e4; -- This is 23451.2
Length : MY_FLOAT := 25.123;
Factor : MY_FLOAT := 8.89;
Cover : MY_LONG_FLOAT;
What : BOOLEAN;
Index : INTEGER := 4;
package Int_IO is new Ada.Text_IO.Integer_IO(INTEGER);
use Int_IO;
package Flt_IO is new Ada.Text_IO.Float_IO(MY_FLOAT);
use Flt_IO;
begin
-- Arithmetic float operations
Area := Length + Factor + 12.56;
Area := Length - Factor - 12.56;
Area := Length * Factor * 2#111.0#; -- this is decimal 7.0
Area := Length / Factor;
Area := Length ** 3;
Area := Length ** (-3);
Area := Length ** Index;
-- Arithmetic logical compares
What := Length = Factor;
What := Length /= Factor;
What := Length > Factor;
What := Length >= Factor;
What := Length < Factor;
What := Length <= Factor;
Area := 0.0031 + (0.027_3 * TWO_PI) / (Length ** (-Index/2));
Cover := 27.3 * TWO_PI * MY_LONG_FLOAT(Area);
Put("Area is now ");
Put(Area);
Put(Area,5);
New_Line;
Put("Area is now ");
Put(Area,5,5);
Put(Area,5,5,0);
New_Line;
Put("MY_FLOAT'DIGITS = ");
Put(MY_FLOAT'DIGITS);
New_Line;
Put("MY_FLOAT'BASE'FIRST = ");
Put(MY_FLOAT'BASE'FIRST);
New_Line;
Put("MY_FLOAT'BASE'LAST = ");
Put(MY_FLOAT'BASE'LAST);
New_Line;
end FloatVar;
-- Result of execution
-- Area is now 1.082677E+02 1.082677E+02
-- Area is now 1.08268E+02 108.26771
-- MY_FLOAT'DIGITS = 7
-- MY_FLOAT'BASE'FIRST = -1.797693E+308
-- MY_FLOAT'BASE'LAST = 1.797693E+308
-- Chapter 6 - Program 8
with Ada.Text_IO;
use Ada.Text_IO;
procedure Fixed is
COARSE_PI : constant := 3.15;
type MY_FIXED is delta 0.1 range -40.0..120.0;
type ANGLE is delta 0.05 range -COARSE_PI..COARSE_PI;
Theta, Omega, Phi : ANGLE := 0.50;
Funny_Value : MY_FIXED;
package Int_IO is new Ada.Text_IO.Integer_IO(INTEGER);
use Int_IO;
package Fix_IO is new Ada.Text_IO.Fixed_IO(MY_FIXED);
use Fix_IO;
package Fix2_IO is new Ada.Text_IO.Fixed_IO(ANGLE);
use Fix2_IO;
package Flt_IO is new Ada.Text_IO.Float_IO(FLOAT);
use Flt_IO;
begin
Put("Theta starts off with the value");
Put(Theta, 5, 2, 0);
New_Line(2);
Theta := Omega + Phi;
Theta := Omega - Phi;
Theta := 5 * Omega - 2 * Phi;
Theta := ANGLE(Omega * Phi);
Theta := ANGLE(3 * Omega / Phi);
Theta := abs(Omega - 3 * Phi);
Funny_Value := 5.1;
for Index in 1..10 loop
Put("Funny_Value is now");
Put(Funny_Value, 5, 1, 0);
Put(Funny_Value, 5, 5, 0);
Funny_Value := MY_FIXED(Funny_Value * MY_FIXED(1.1));
New_Line;
end loop;
New_Line;
Put("MY_FIXED'DELTA = ");
Put(MY_FIXED(MY_FIXED'DELTA));
New_Line;
Put("MY_FIXED'FIRST = ");
Put(MY_FIXED'FIRST);
New_Line;
Put("MY_FIXED'LAST = ");
Put(MY_FIXED'LAST);
New_Line;
end Fixed;
-- Result of execution
-- Theta starts off with the value 0.50
--
-- Funny_Value is now 5.1 5.10156
-- Funny_Value is now 5.6 5.61719
-- Funny_Value is now 6.2 6.18750
-- Funny_Value is now 6.8 6.81250
-- Funny_Value is now 7.5 7.50391
-- Funny_Value is now 8.3 8.26562
-- Funny_Value is now 9.1 9.10156
-- Funny_Value is now 10.0 10.02344
-- Funny_Value is now 11.0 11.03906
-- Funny_Value is now 12.2 12.16016
--
-- MY_FIXED'DELTA = 0.1
-- MY_FIXED'FIRST = -40.0
-- MY_FIXED'LAST = 120.0
-- Chapter 6 - Program 9
with Ada.Text_IO;
use Ada.Text_IO;
procedure Decimal is
type DOLLAR is delta 0.01 digits 8 range 0.00..1_000.00;
type DIMES is delta 0.1 digits 6;
Amount : DOLLAR := 3.00;
Coins : DIMES := 1.20;
package Dec_IO is new Ada.Text_IO.Decimal_IO(DOLLAR);
use Dec_IO;
package Dime_IO is new Ada.Text_IO.Decimal_IO(DIMES);
use Dime_IO;
begin
for Index in 1..8 loop
Amount := Amount + 1.23;
Put(Amount);
Coins := Coins + 3.70;
Put(Coins);
New_Line;
end loop;
end Decimal;
-- Result of execution
--
-- 4.23 4.9
-- 5.46 8.6
-- 6.69 12.3
-- 7.92 16.0
-- 9.15 19.7
-- 10.38 23.4
-- 11.61 27.1
-- 12.84 30.8
-- Chapter 6 - Program 10
with Ada.Text_IO;
use Ada.Text_IO;
procedure MixTypes is
PI : constant := 3.1416;
TWO_PI : constant := 2 * PI;
type MY_FLOAT is digits 7;
Size : MY_FLOAT;
type MY_FIXED is delta 0.1 range -40.0..120.0;
Temperature : MY_FIXED;
Index : INTEGER;
begin
Size := TWO_PI;
Temperature := 2 * TWO_PI;
Temperature := 3 * Temperature;
Temperature := MY_FIXED(12.0 * TWO_PI);
Size := MY_FLOAT(Temperature) + PI;
Size := MY_FLOAT(Temperature + PI);
Index := INTEGER(Size + MY_FLOAT(Temperature) + PI);
Index := INTEGER(Size) + INTEGER(Temperature) + INTEGER(PI);
Index := INTEGER(Size + PI) + INTEGER(Temperature);
end MixTypes;
-- Result of execution
-- (No output from this program.)
-- Chapter 6 - Programming exercise 1
with Ada.Text_IO;
use Ada.Text_IO;
procedure Ch06_1 is
package Long_IO is new Ada.Text_IO.Integer_IO(LONG_INTEGER);
use Long_IO;
package Short_IO is new Ada.Text_IO.Integer_IO(SHORT_INTEGER);
use Short_IO;
Long_Variable : LONG_INTEGER;
Short_Variable : SHORT_INTEGER;
begin
Put_Line("For LONG_INTEGER,");
Put(LONG_INTEGER'FIRST);
Put_Line(" is the minimum value");
Put(LONG_INTEGER'LAST);
Put_Line(" is the maximum value");
Put_Line("For SHORT_INTEGER,");
Put(SHORT_INTEGER'FIRST);
Put_Line(" is the minimum value");
Put(SHORT_INTEGER'LAST);
Put_Line(" is the maximum value");
end Ch06_1;
-- Result of execution
-- For LONG_INTEGER
-- -2147483648 is the minimum value
-- 2147483647 is the maximum value
-- For SHORT_INTEGER
-- -32768 is the minimum value
-- 32767 is the maximum value
-- Chapter 6 - Programming exercise 2
with Ada.Text_IO;
use Ada.Text_IO;
procedure Ch06_2 is
package Long_IO is new Ada.Text_IO.Float_IO(LONG_FLOAT);
use Long_IO;
package Short_IO is new Ada.Text_IO.Float_IO(SHORT_FLOAT);
use Short_IO;
Long_Variable : LONG_FLOAT;
Short_Variable : SHORT_FLOAT;
begin
Put_Line("For LONG_FLOAT,");
Put(LONG_FLOAT'FIRST);
Put_Line(" is the minimum value");
Put(LONG_FLOAT'LAST);
Put_Line(" is the maximum value");
Put_Line("For SHORT_FLOAT,");
Put(SHORT_FLOAT'FIRST);
Put_Line(" is the minimum value");
Put(SHORT_FLOAT'LAST);
Put_Line(" is the maximum value");
end Ch06_2;
-- Result of execution
-- For LONG_FLOAT
-- ? is the minimum value
-- ? is the maximum value
-- For SHORT_FLOAT
-- ? is the minimum value
-- ? is the maximum value
-- Chapter 6 - Programming exercise 3
with Ada.Text_IO;
use Ada.Text_IO;
procedure Ch06_3 is
type MY_ENUM is (YES, NO, MAYBE, WHY, POSSIBLY);
Answer : MY_ENUM := YES;
begin
Answer := MY_ENUM'PRED(Answer);
end Ch06_3;
-- Result of execution
-- Exception never handled: constraint_error
-- Value 0 out of range 1..4.
-- (This means that since the range of the enumerated variable
-- only covers four values, trying to assign the value of zero,
-- which is one less than the allowed minimum causes us to go
-- out of the allowable range. )
-- Chapter 7 - Program 1
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure DerTypes is
type LITTLE_INT is range -24..17;
type TINY_INT is range -3..2;
type POS_INT is range 25..38;
type TINY_POS is new POS_INT range 25..30;
type SALAD_INT is new INTEGER;
type ANIMAL_INT is new INTEGER;
type TREE_INT is new INTEGER range -557..1098;
Salad : SALAD_INT;
Lettuce : SALAD_INT := 22;
Tomatoes : SALAD_INT := 14;
Animals : ANIMAL_INT;
Dogs : ANIMAL_INT := 3;
Cats : ANIMAL_INT := 4;
Trees : TREE_INT;
Oak : TREE_INT := 12;
Coconut : TREE_INT := 8;
Count : INTEGER;
begin
Salad := Lettuce + Tomatoes;
Animals := Dogs + Cats;
Trees := Oak + Coconut + TREE_INT(Animals);
Count := INTEGER(Trees) + INTEGER(Salad);
Salad := SALAD_INT(Dogs) * Tomatoes +
SALAD_INT(Cats) * SALAD_INT(Oak) +
SALAD_INT(Count);
Put("The 1st Salad calculation is ");
Put(INTEGER(Salad));
New_Line;
Salad := SALAD_INT(Dogs * ANIMAL_INT(Tomatoes) +
Cats * ANIMAL_INT(Oak) +
ANIMAL_INT(Count));
Put("The 2nd Salad calculation is ");
Put(INTEGER(Salad));
New_Line;
end DerTypes;
-- Result of execution
-- The 1st Salad calculation is 153
-- The 2nd Salad calculation is 153
-- Chapter 7 - Program 2
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure DerSubs is
type NEW_INT is new INTEGER range 12..127;
type NEW_INT_TYPE is new INTEGER;
subtype SUB_INT is NEW_INT;
subtype NEW_SUBTYPE is NEW_INT range 12..127;
type DER_SUB is new NEW_SUBTYPE range 12..32;
Arrow, Dart : NEW_INT;
Size : NEW_INT_TYPE;
Thing : INTEGER := 15;
Point : NEW_SUBTYPE;
begin
Size := 10;
Arrow := 23;
Dart := 2 * Arrow - 25;
Dart := Arrow + 2 * (NEW_INT(Size + 2) + NEW_INT(Thing));
Dart := Arrow + 2 * NEW_INT(Size + NEW_INT_TYPE(Thing));
Point := Arrow + Dart;
end DerSubs;
-- Result of execution
-- (No output from this program.)
-- Chapter 7 - Program 3
with Ada.Text_IO;
use Ada.Text_IO;
procedure MoreDers is
-- Some floating point types
type NEW_FLOAT1 is digits 5;
type NEW_FLOAT2 is digits 5 range 1.0..12.0;
type DER_FLOAT is new FLOAT;
type LIM_FLOAT is new FLOAT range 0.0..555.5;
subtype SUB_FLOAT is DER_FLOAT range -2.3..12.8;
-- Some fixed point types
type NEW_FIXED1 is delta 0.5 range 1.0..12.0;
type NEW_FIXED2 is delta 0.05 range 1.0..12.0;
type DER_FIXED is new NEW_FIXED1;
type LIM_FIXED is new NEW_FIXED1 range 1.0..5.5;
subtype SUB_FIXED is DER_FIXED range 2.1..2.8;
-- Some CHARACTER types
type DER_CHAR is new CHARACTER;
type ALPHA_CHAR is new CHARACTER range 'A'..'Z';
subtype HEX_CHAR is ALPHA_CHAR range 'A'..'F';
-- Some enumerated types
type DAY is (MON, TUE, WED, THU, FRI, SAT, SUN);
type WEEKDAY is new DAY range MON..FRI;
subtype BOWLING_DAY is WEEKDAY range WED..THU;
-- Some floating point objects
Direction : FLOAT;
Speed : DER_FLOAT := 100.0;
Length : LIM_FLOAT := 72.41;
Size : SUB_FLOAT := 4.3;
begin
Direction := 1.2 + FLOAT(Length + LIM_FLOAT(Speed * Size));
end MoreDers;
-- Result of execution
-- (There is no output from this program)
-- Chapter 7 - Programming exercise 1
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure CH07_1 is
type LITTLE_INT is range -24..17;
type TINY_INT is range -3..2;
type POS_INT is range 25..38;
type TINY_POS is new POS_INT range 25..30;
type SALAD_INT is new INTEGER;
type ANIMAL_INT is new INTEGER;
type TREE_INT is new INTEGER range -557..1098;
package Salad_Int_IO is new Ada.Text_IO.Integer_IO(SALAD_INT);
use Salad_Int_IO;
Salad : SALAD_INT;
Lettuce : SALAD_INT := 22;
Tomatoes : SALAD_INT := 14;
Animals : ANIMAL_INT;
Dogs : ANIMAL_INT := 3;
Cats : ANIMAL_INT := 4;
Trees : TREE_INT;
Oak : TREE_INT := 12;
Coconut : TREE_INT := 8;
Count : INTEGER;
begin
Salad := Lettuce + Tomatoes;
Animals := Dogs + Cats;
Trees := Oak + Coconut + TREE_INT(Animals);
Count := INTEGER(Trees) + INTEGER(Salad);
Salad := SALAD_INT(Dogs) * Tomatoes +
SALAD_INT(Cats) * SALAD_INT(Oak) +
SALAD_INT(Count);
Put("The 1st Salad calculation is ");
Put(Salad);
New_Line;
Salad := SALAD_INT(Dogs * ANIMAL_INT(Tomatoes) +
Cats * ANIMAL_INT(Oak) +
ANIMAL_INT(Count));
Put("The 2nd Salad calculation is ");
Put(Salad);
New_Line;
end CH07_1;
-- Result of execution
-- The 1st Salad calculation is 153
-- The 2nd Salad calculation is 153
-- Chapter 8 - Program 1
with Ada.Text_IO;
use Ada.Text_IO;
procedure Proced1 is
procedure Write_A_Line is
begin
Put("This is a line of text.");
New_Line;
end Write_A_Line;
begin
Write_A_Line;
Write_A_Line;
end Proced1;
-- Result of execution
-- This is a line of text.
-- This is a line of text.
-- Chapter 8 - Program 2
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Proced2 is
Counter : INTEGER;
procedure Write_A_Header is
begin
Counter := 1;
Put("This is the heading for this little program.");
New_Line(2);
end Write_A_Header;
procedure Write_And_Increment is
begin
Put("This is line number");
Put(Counter, 2);
Put_Line(" of this program.");
Counter := Counter + 1;
end Write_And_Increment;
procedure Write_An_Ending_Statement is
begin
New_Line;
Put_Line("This is the end of this little program.");
end Write_An_Ending_Statement;
begin
Write_A_Header;
for Index in 1..7 loop
Write_And_Increment;
end loop;
Write_An_Ending_Statement;
end Proced2;
-- Result of execution
-- This is the heading for this little program.
--
-- This is line number 1 of this program.
-- This is line number 2 of this program.
-- This is line number 3 of this program.
-- This is line number 4 of this program.
-- This is line number 5 of this program.
-- This is line number 6 of this program.
-- This is line number 7 of this program.
--
-- This is the end of this little program.
-- Chapter 8 - Program 3
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Proced3 is
Dogs, Cats, Animals : INTEGER;
-- This is a procedure specification
procedure Total_Number_Of_Animals(Variety1 : in INTEGER;
Variety2 : in INTEGER;
Total : out INTEGER);
-- This is a procedure body
procedure Total_Number_Of_Animals(Variety1 : in INTEGER;
Variety2 : in INTEGER;
Total : out INTEGER) is
begin
Total := Variety1 + Variety2;
end Total_Number_Of_Animals;
begin
Dogs := 3;
Cats := 4;
Total_Number_Of_Animals(Dogs, Cats, Animals);
Put("The total number of animals is");
Put(Animals, 3);
New_Line;
end Proced3;
-- Result of execution
-- The total number of animals is 7
-- Chapter 8 - Program 4
with Ada.Text_IO;
use Ada.Text_IO;
procedure Calling is
procedure One is
begin
Put("This is procedure One talking.");
New_Line;
end One;
procedure Two is
begin
Put("This is procedure Two talking.");
New_Line;
One;
end Two;
procedure Three is
begin
Put("This is procedure Three talking.");
New_Line;
Two;
One;
end Three;
begin
One;
Two;
Three;
end Calling;
-- Result of execution
-- This is procedure One talking.
-- This is procedure Two talking.
-- This is procedure One talking.
-- This is procedure Three talking.
-- This is procedure Two talking.
-- This is procedure One talking.
-- This is procedure One talking.
-- Chapter 8 - Program 5
with Ada.Text_IO;
use Ada.Text_IO;
procedure Nesting is
procedure Triple is
procedure Second_Layer is
procedure Bottom_Layer is
begin
Put_Line("This is the Bottom Layer talking.");
end Bottom_Layer;
begin
Put_Line("This is the Second Layer talking.");
Bottom_Layer;
Put_Line("We are back up to the Second Layer.");
end Second_Layer;
begin
Put_Line("This is procedure Triple talking to you.");
Second_Layer;
Put_Line("We are back up to the procedure named Triple.");
end Triple;
begin
Put_Line("Start the triple nesting here.");
Triple;
Put_Line("Finished, and back to the top level.");
end Nesting;
-- Result of execution
-- Start the triple nesting here.
-- This is procedure Triple talking to you.
-- This is the Second Layer talking.
-- This is the Bottom Layer talking.
-- We are back up to the Second Layer.
-- We are back up to the procedure named Triple.
-- Finished, and back to the top level.
-- Chapter 8 - Program 6
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Funct is
Twelve : INTEGER := 12;
Sum : INTEGER;
-- This is a function specification
function Square(Val : INTEGER) return INTEGER;
-- This is a function body
function Square(Val : INTEGER) return INTEGER is
begin
return Val * Val;
end Square;
function Sum_Of_Numbers(Val1, Val2 : INTEGER) return INTEGER is
begin
return Val1 + Val2;
end Sum_Of_Numbers;
begin
Put("The square of 12 is");
Put(Square(Twelve), 4);
New_line;
Sum := Sum_Of_Numbers(Twelve, 12);
Put("The sum of 12 and 12 is");
Put(Sum, 4);
New_Line;
end Funct;
-- Result of execution
-- The square of 12 is 144
-- The sum of 12 and 12 is 24
-- Chapter 8 - Program 7
-- This is a program to return an odd square of a number. The result
-- will have the sign of the original number, and the magnitude of
-- the square of the original number. The entire program can be
-- written in one line (i.e. - Result := Index * abs(Index); ), but
-- the purpose of the program is to illustrate how to put various
-- procedures and functions together to build a useable program.
-- Various levels of nesting are illustrated along with several
-- parameters being used in procedure calls.
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure OddSqre is
Result : INTEGER;
function Square_The_Number(Number_To_Square : in INTEGER)
return INTEGER is
begin
return Number_To_Square * Number_To_Square;
end Square_The_Number;
procedure Square_And_Keep_Sign(Input_Value : in INTEGER;
Funny_Result : out INTEGER) is
procedure Do_A_Negative_Number(InVal : in INTEGER;
OutVal : out INTEGER) is
begin
OutVal := -Square_The_Number(InVal);
end Do_A_Negative_Number;
begin
if Input_Value < 0 then
Do_A_Negative_Number(Input_Value, Funny_Result);
return;
elsif Input_Value = 0 then
Funny_Result := 0;
return;
else
Funny_Result := Square_The_Number(Input_Value);
return;
end if;
end Square_And_Keep_Sign;
begin
for Index in -3..4 loop
Square_And_Keep_Sign(Index, Result);
Put("The Odd Square of");
Put(Index, 3);
Put(" is");
Put(Result, 5);
New_Line;
end loop;
end OddSqre;
-- Result of execution
-- The Odd Square of -3 is -9
-- The Odd Square of -2 is -4
-- The Odd Square of -1 is -1
-- The Odd Square of 0 is 0
-- The Odd Square of 1 is 1
-- The Odd Square of 2 is 4
-- The Odd Square of 3 is 9
-- The Odd Square of 4 is 16
-- Chapter 8 - Program 8
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Overload is
Int_Dat : INTEGER;
Flt_Dat : FLOAT;
function Raise_To_Power(Index : INTEGER) return INTEGER is
begin
Put_Line("In the INTEGER function.");
return Index * Index;
end Raise_To_Power;
function Raise_To_Power(Value : FLOAT) return FLOAT is
begin
Put_Line("In the FLOAT function.");
return Value * Value * Value;
end Raise_To_Power;
procedure Raise_To_Power(Index : in INTEGER;
Result : out INTEGER) is
begin
Put_Line("In the INTEGER procedure.");
Result := Index * Index * Index;
end Raise_To_Power;
procedure Raise_To_Power(Value : in FLOAT;
Result : out FLOAT) is
begin
Put_Line("In the FLOAT procedure.");
Result := Value * Value;
end Raise_To_Power;
begin
Int_Dat := Raise_To_Power(2); -- uses INTEGER function
Flt_Dat := Raise_To_Power(3.2); -- uses FLOAT function
Raise_To_Power(3, Int_Dat); -- uses INTEGER procedure
Raise_To_Power(2.73, Flt_Dat); -- uses FLOAT procedure
Int_Dat := 2;
-- In the following statement,
-- the function returns 2 squared, or 4
-- and the procedure cubes it to 64.
Raise_To_Power(Raise_To_Power(Int_Dat), Int_Dat);
Put("The result is ");
Put(Int_Dat, 4);
New_Line;
end Overload;
-- Result of execution
-- In the INTEGER function.
-- In the FLOAT function.
-- In the INTEGER procedure.
-- In the FLOAT procedure.
-- In the INTEGER function.
-- In the INTEGER procedure.
-- The result is 64
-- Chapter 8 - Programming exercixe 1
-- Centigrade to Farenheit temperature table
--
-- This program generates a list of Centigrade and Farenheit
-- temperatures with a note at the freezing point of water
-- and another note at the boiling point of water.
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure CH08_1 is
Centigrade, Farenheit : INTEGER;
procedure Cent_To_Faren(Cent : in INTEGER;
Faren : out INTEGER) is
begin
Faren := 32 + Cent * 9 / 5;
end Cent_To_Faren;
begin
Put("Centigrade to Farenheit temperature table");
New_Line(2);
for Count in INTEGER range -2..12 loop
Centigrade := 10 * Count;
Cent_To_Faren(Centigrade,Farenheit);
Put("C =");
Put(Centigrade,5);
Put(" F =");
Put(Farenheit,5);
if Centigrade = 0 then
Put(" Freezing point of water");
end if;
if Centigrade = 100 then
Put(" Boiling point of water");
end if;
New_Line;
end loop;
end CH08_1;
-- Result of execution
-- Centigrade to Farenheit temperature table
--
-- C = -20 F = -4
-- C = -10 F = 14
-- C = 0 F = 32 Freezing point of water
-- C = 10 F = 50
-- C = 20 F = 68
-- C = 30 F = 86
-- C = 40 F = 104
-- C = 50 F = 122
-- C = 60 F = 140
-- C = 70 F = 158
-- C = 80 F = 176
-- C = 90 F = 194
-- C = 100 F = 212 Boiling point of water
-- C = 110 F = 230
-- C = 120 F = 248
-- Chapter 8 - Programming exercise 2
-- Centigrade to Farenheit temperature table
--
-- This program generates a list of Centigrade and Farenheit
-- temperatures with a note at the freezing point of water
-- and another note at the boiling point of water.
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure CH08_2 is
Centigrade, Farenheit : INTEGER;
function Cent_To_Faren(Cent : INTEGER) return INTEGER is
begin
return (32 + Cent * 9 / 5);
end Cent_To_Faren;
begin
Put("Centigrade to Farenheit temperature table");
New_Line(2);
for Count in INTEGER range -2..12 loop
Centigrade := 10 * Count;
Farenheit := Cent_To_Faren(Centigrade);
Put("C =");
Put(Centigrade,5);
Put(" F =");
Put(Farenheit,5);
if Centigrade = 0 then
Put(" Freezing point of water");
end if;
if Centigrade = 100 then
Put(" Boiling point of water");
end if;
New_Line;
end loop;
end CH08_2;
-- Result of execution
-- Centigrade to Farenheit temperature table
--
-- C = -20 F = -4
-- C = -10 F = 14
-- C = 0 F = 32 Freezing point of water
-- C = 10 F = 50
-- C = 20 F = 68
-- C = 30 F = 86
-- C = 40 F = 104
-- C = 50 F = 122
-- C = 60 F = 140
-- C = 70 F = 158
-- C = 80 F = 176
-- C = 90 F = 194
-- C = 100 F = 212 Boiling point of water
-- C = 110 F = 230
-- C = 120 F = 248
-- Chapter 8 - Programming exercise 3
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure CH08_3 is
Int_Dat : INTEGER;
Flt_Dat : FLOAT;
function Raise_To_Power(Index : INTEGER) return INTEGER is
begin
Put_Line("In the INTEGER function.");
return Index * Index;
end Raise_To_Power;
function Raise_To_Power(Value : FLOAT) return FLOAT is
begin
Put_Line("In the FLOAT function.");
return Value * Value * Value;
end Raise_To_Power;
function Raise_To_Power(Value : INTEGER) return FLOAT is
begin
Put_Line("In the new function.");
return FLOAT(Value * Value * Value);
end Raise_To_Power;
procedure Raise_To_Power(Index : in INTEGER;
Result : out INTEGER) is
begin
Put_Line("In the INTEGER procedure.");
Result := Index * Index * Index;
end Raise_To_Power;
procedure Raise_To_Power(Value : in FLOAT;
Result : out FLOAT) is
begin
Put_Line("In the FLOAT procedure.");
Result := Value * Value;
end Raise_To_Power;
begin
Int_Dat := Raise_To_Power(2); -- uses INTEGER function
Flt_Dat := Raise_To_Power(3); -- uses new function
Raise_To_Power(3,Int_Dat); -- uses INTEGER procedure
Raise_To_Power(2.73,Flt_Dat); -- uses FLOAT procedure
Int_Dat := 2;
-- In the following statement,
-- the function returns 2 squared, or 4
-- and the procedure cubes it to 64.
Raise_To_Power(Raise_To_Power(Int_Dat),Int_Dat);
Put("The result is ");
Put(Int_Dat);
New_Line;
end CH08_3;
-- Result of execution
-- In the INTEGER function.
-- In the new function.
-- In the INTEGER procedure.
-- In the FLOAT procedure.
-- In the INTEGER function.
-- In the INTEGER procedure.
-- The result is 64
-- Chapter 9 - Program 1
procedure Scope is
Count : INTEGER;
procedure Level1 is
Index : INTEGER;
procedure Level2 is
Count : INTEGER;
begin
null;
end Level2;
procedure Level2_Prime is
Data : INTEGER;
begin
null;
end Level2_Prime;
begin
null;
end Level1;
procedure Other_Level1 is
begin
null;
end Other_Level1;
begin
null;
end Scope;
-- Result of execution
-- (No output from this program.)
-- Chapter 9 - Program 2
procedure Scope2 is
Count, Index : INTEGER;
procedure Level1 is
Index, Count : INTEGER;
procedure Level2 is
Count : INTEGER;
begin
Count := -- Count from line 10
Scope2.Count; -- Count from line 4
end Level2;
procedure Level2_Prime is
Data, Index, Count : INTEGER;
Outer_Index : INTEGER renames Scope2.Level1.Index;
begin
Count := Index -- Count from line 17
+ Scope2.Level1.Count; -- Count from line 7
Index := -- Index from line 17
Scope2.Level1.Index + -- Index from line 7
Scope2.Index; -- Index from line 4
Index := -- Index from line 17
Outer_Index + -- Index from line 7
Scope2.Index; -- Index from line 4
end Level2_Prime;
begin
null;
end Level1;
procedure Other_Level1 is
begin
Count := Index; -- Both from line 4
end Other_Level1;
begin
Count := Index; -- Both from line 4
end Scope2;
-- Result of execution
-- (No output from this program)
-- Chapter 9 - Program 3
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Blocks is
Index, Count : INTEGER;
begin
Index := 27;
Count := 33;
Put("In the main block - values are");
Put(Index, 5); -- Blocks.Index
Put(Count, 5); -- Blocks.Count
New_Line;
declare
Index, Stuff : INTEGER := -345;
begin
Index := 157;
Put("In the embedded block - values are");
Put(Blocks.Index, 5); -- Blocks.Index
Put(Index, 5); -- local Index
Put(Stuff, 5); -- local Stuff
Put(Count, 5); -- Blocks.Count
New_Line;
end;
Put("Back to the main block - values are");
Put(Index, 5); -- Blocks.Index
Put(Count, 5); -- Blocks.Count
New_Line;
Who: -- Block name
declare
Index, Stuff : INTEGER := -345;
begin
Index := 157;
Put("In the block named Who - values are");
Put(Blocks.Index, 5); -- Blocks.Index
Put(Index, 5); -- Who.Index
Put(Who.Index, 5); -- Who.Index
Put(Stuff, 5); -- Who.Stuff
Put(Who.Stuff, 5); -- Who.Stuff
Put(Count, 5); -- Blocks.Count
New_Line;
end Who;
Put("Back to the main block - values are");
Put(Index, 5); -- Blocks.Index
Put(Count, 5); -- Blocks.Count
New_Line;
end Blocks;
-- Result of execution
-- In the main block - values are 27 33
-- In the embedded block - values are 27 157 -345 33
-- Back to the main block - values are 27 33
-- In the block named Who - values are 27 157 157 -345 -345 33
-- Back to the main block - values are 27 33
-- Chapter 9 - Program 4
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Automatc is
Dog, Cat : INTEGER;
Pig, Cow : FLOAT;
begin
for Index in 1..10 loop
Put("The value of Index is");
Put(Index, 3);
declare
START : constant INTEGER := Index;
STOP : constant INTEGER := START + 5;
Count_Stuff : INTEGER;
begin
Count_Stuff := START + STOP + Index + 222;
Put(" --->");
for Index in START..STOP loop
Put(Index, 5);
end loop;
end;
New_Line;
end loop;
end Automatc;
-- Result of execution
-- The value of Index is 1 ---> 1 2 3 4 5 6
-- The value of Index is 2 ---> 2 3 4 5 6 7
-- The value of Index is 3 ---> 3 4 5 6 7 8
-- The value of Index is 4 ---> 4 5 6 7 8 9
-- The value of Index is 5 ---> 5 6 7 8 9 10
-- The value of Index is 6 ---> 6 7 8 9 10 11
-- The value of Index is 7 ---> 7 8 9 10 11 12
-- The value of Index is 8 ---> 8 9 10 11 12 13
-- The value of Index is 9 ---> 9 10 11 12 13 14
-- The value of Index is 10 ---> 10 11 12 13 14 15
-- Chapter 9 - Programming exercise 1
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure CH09_1 is
Index, Count : INTEGER;
begin
Index := 27;
Count := 33;
Put("In the main block - values are");
Put(Index, 5); -- CH09_1.Index
Put(Count, 5); -- CH09_1.Count
New_Line;
declare
Index, Stuff : INTEGER := -345;
procedure Output_A_Line is
begin
Put_Line("This is in the new block procedure");
end Output_A_Line;
begin
Index := 157;
Put("In the embedded block - values are");
Put(CH09_1.Index, 5); -- CH09_1.Index
Put(Index, 5); -- local Index
Put(Stuff, 5); -- local Stuff
Put(Count, 5); -- CH09_1.Count
New_Line;
Output_A_Line;
end;
Put("Back to the main block - values are");
Put(Index, 5); -- CH09_1.Index
Put(Count, 5); -- CH09_1.Count
New_Line;
Who: -- Block name
declare
Index, Stuff : INTEGER := -345;
begin
Index := 157;
Put("In the block named Who - values are");
Put(CH09_1.Index, 5); -- CH09_1.Index
Put(Index, 5); -- Who.Index
Put(Who.Index, 5); -- Who.Index
Put(Stuff, 5); -- Who.Stuff
Put(Who.Stuff, 5); -- Who.Stuff
Put(Count, 5); -- CH09_1.Count
New_Line;
end Who;
Put("Back to the main block - values are");
Put(Index, 5); -- CH09_1.Index
Put(Count, 5); -- CH09_1.Count
New_Line;
end CH09_1;
-- Result of execution
-- In the main block - values are 27 33
-- In the embedded block - values are 27 157 -345 33
-- This is in the new block procedure
-- Back to the main block - values are 27 33
-- In the block named Who - values are 27 157 157 -345 -345 33
-- Back to the main block - values are 27 33
-- Chapter 9 - Programming exercise 2
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure CH09_2 is
Dog, Cat : INTEGER;
Pig, Cow : FLOAT;
begin
My_Loop:
for Index in 1..10 loop
Put("The value of Index is");
Put(Index, 3);
declare
START : constant INTEGER := Index;
STOP : constant INTEGER := START + 5;
Count_Stuff : INTEGER;
begin
Count_Stuff := START + STOP + Index + 222;
Put(" --->");
for Index in START..STOP loop
Put(My_Loop.Index, 5);
end loop;
end;
New_Line;
end loop My_Loop;
end CH09_2;
-- Result of execution
-- The value of Index is 1 ---> 1 1 1 1 1 1
-- The value of Index is 2 ---> 2 2 2 2 2 2
-- The value of Index is 3 ---> 3 3 3 3 3 3
-- The value of Index is 4 ---> 4 4 4 4 4 4
-- The value of Index is 5 ---> 5 5 5 5 5 5
-- The value of Index is 6 ---> 6 6 6 6 6 6
-- The value of Index is 7 ---> 7 7 7 7 7 7
-- The value of Index is 8 ---> 8 8 8 8 8 8
-- The value of Index is 9 ---> 9 9 9 9 9 9
-- The value of Index is 10 ---> 10 10 10 10 10 10
-- Chapter 10 - Program 1
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Array1 is
N : INTEGER := 10;
Dummy1 : array(INTEGER range 1..7) of BOOLEAN;
Dummy2 : array(INTEGER range -21..N) of BOOLEAN;
Dummy3 : array(-21..N) of BOOLEAN;
type MY_ARRAY is array(1..5) of INTEGER;
Total : MY_ARRAY;
First : MY_ARRAY;
Second : MY_ARRAY;
Funny : array(1..5) of INTEGER;
X,Y : array(12..27) of INTEGER;
Fourth_Value : INTEGER renames First(4);
begin
First(1) := 12;
First(2) := 16;
First(3) := First(2) - First(1);
Fourth_Value := -13;
First(5) := 16 - 2 * First(2);
for Index in 1..5 loop
Second(Index) := 3 * Index + 77;
end loop;
Total := First;
if Total = First then
Put("Both arrays are the same size and contain ");
Put_Line("the same values in all elements.");
end if;
for Index in 1..5 loop
Total(Index) := Total(Index) + Second(Index);
Funny(Index) := Total(Index) + First(6 - Index);
Put("The array values are");
Put(Total(Index), 6);
Put(Funny(Index), 6);
New_Line;
end loop;
end Array1;
-- Result of execution
-- Both arrays are the same size and contain the same values in...
-- The array values are 92 76
-- The array values are 99 86
-- The array values are 90 94
-- The array values are 76 92
-- The array values are 76 88
-- Chapter 10 - Program 2
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Slice is
type MY_ARRAY is array(1..12) of INTEGER;
First, Second : MY_ARRAY;
Funny : array(1..33) of INTEGER;
begin
for Index in 1..33 loop
Funny(Index) := 100 + Index * 2;
end loop;
for Index in 1..12 loop
First(Index) := Index;
end loop;
Second(1..5) := First(3..7);
Second(1..4) := First(6..9);
Second(7..12) := First(3..8);
First(2..9) := First(5..12);
First(1..12) := MY_ARRAY(Funny(1..12));
First(1..12) := MY_ARRAY(Funny(16..27));
for Index in 1..12 loop
Put("The array named First has the values ");
Put(First(Index));
New_Line;
end loop;
end Slice;
-- Result of execution
-- The array named First has the values 132
-- The array named First has the values 134
-- The array named First has the values 136
-- The array named First has the values 138
-- The array named First has the values 140
-- The array named First has the values 142
-- The array named First has the values 144
-- The array named First has the values 146
-- The array named First has the values 148
-- The array named First has the values 150
-- The array named First has the values 152
-- The array named First has the values 154
-- Chapter 10 - Program 3
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure MultAry1 is
type MATRIX is array(INTEGER range 1..3,
INTEGER range 1..4) of INTEGER;
Square_Board : MATRIX;
Checker_Board : MATRIX;
Chess_Board : array(INTEGER range 1..3,
INTEGER range 1..4) of INTEGER;
Across, Over : INTEGER;
begin
for Across in 1..3 loop
for Over in 1..4 loop
Square_Board(Across, Over) := Across * Over;
Chess_Board(Across, Over) := 0;
end loop;
end loop;
Checker_Board := Square_Board;
Checker_Board(2, 3) := 2;
Checker_Board(Checker_Board(2, 3), 4) := 17;
Checker_Board(3, 3) := Chess_Board(3, 3);
for Across in 1..3 loop
for Over in 1..4 loop
Put(Checker_Board(Across, Over), 5);
end loop;
New_Line;
end loop;
end MultAry1;
-- Result of execution
-- 1 2 3 4
-- 2 4 2 17
-- 3 6 0 12
-- Chapter 10 - Program 4
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure MultAry2 is
SIZE : constant := 3;
NEXT : constant := SIZE + 1;
type MATRIX is array(INTEGER range 1..SIZE,
INTEGER range 1..NEXT) of INTEGER;
Square_Board : MATRIX;
Checker_Board : MATRIX;
Chess_Board : array(INTEGER range 1..SIZE,
INTEGER range 1..NEXT) of INTEGER;
Across, Over : INTEGER;
begin
for Across in 1..SIZE loop
for Over in 1..NEXT loop
Square_Board(Across, Over) := Across * Over;
Chess_Board(Across, Over) := 0;
end loop;
end loop;
Checker_Board := Square_Board;
Checker_Board(2, 3) := 2;
Checker_Board(Checker_Board(2, 3), 4) := 17;
Checker_Board(3, 3) := Chess_Board(3, 3);
for Across in 1..SIZE loop
for Over in 1..NEXT loop
Put(Checker_Board(Across, Over), 5);
end loop;
New_Line;
end loop;
end MultAry2;
-- Result of execution
-- 1 2 3 4
-- 2 4 2 17
-- 3 6 0 12
-- Chapter 10 - Program 5
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure MultAry3 is
SIZE : constant := 3;
NEXT : constant := SIZE + 1;
type MATRIX is array(INTEGER range 1..SIZE,
INTEGER range 1..NEXT) of INTEGER;
Square_Board : MATRIX;
Checker_Board : MATRIX;
Chess_Board : array(INTEGER range 1..SIZE,
INTEGER range 1..NEXT) of INTEGER;
Across, Over : INTEGER;
begin
for Across in 1..Square_Board'LAST(1) loop
for Over in 1..Square_Board'LAST(2) loop
Square_Board(Across, Over) := Across * Over;
Chess_Board(Across, Over) := 0;
end loop;
end loop;
Checker_Board := Square_Board;
Checker_Board(2, 3) := 2;
Checker_Board(Checker_Board(2, 3), 4) := 17;
Checker_Board(3, 3) := Chess_Board(3, 3);
for Across in Checker_Board'RANGE(1) loop
for Over in Checker_Board'RANGE(2) loop
Put(Checker_Board(Across, Over), 6);
end loop;
New_Line;
end loop;
end MultAry3;
-- Result of execution
-- 1 2 3 4
-- 2 4 2 17
-- 3 6 0 12
-- Chapter 10 - Program 6
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure ArryInit is
type MY_ARRAY is array(1..5) of INTEGER;
Total : MY_ARRAY := (12, 27, -13, 122, 44);
First : MY_ARRAY := (1 => 14, 2 => 57, 3=> 111,
5 => -27, 4 => 21);
Another : MY_ARRAY := (2 => 13, 5 => 57, others => 3);
One_More : MY_ARRAY := (2..4 => 13, 1 | 5 => 27);
type MATRIX is array(INTEGER range 1..3,
INTEGER range 1..4) of INTEGER;
Square_Board : MATRIX := ((4, 7, 3, 5),
(3, 8, 2, 0),
(1, 5, 9, 9));
Checker_Board : MATRIX := (2 => (3, 8, 2, 0),
3 => (1, 5, 9, 9),
1 => (4, 7, 3, 5));
Chess_Board : MATRIX := (2 => (3, 8, 2, 0),
3 => (1, 5, 9, 9),
1 => (4 => 5, 2 => 7, 1 => 4, 3 => 3));
begin
if Square_Board = Checker_Board then
Put_Line("The two arrays are equal.");
end if;
if Chess_Board = Square_Board then
Put_Line(" and so are the other two.");
end if;
end ArryInit;
-- Result of execution
-- The two arrays are equal.
-- and so are the other two.
-- Chapter 10 - Programming exercise 1
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure CH10_1 is
N : INTEGER := 10;
Dummy1 : array(INTEGER range 1..7) of BOOLEAN;
Dummy2 : array(INTEGER range -21..N) of BOOLEAN;
Dummy3 : array(-21..N) of BOOLEAN;
type MY_ARRAY is array(1..5) of INTEGER;
type NEW_ARRAY_TYPE is array(12..27) of INTEGER;
Total : MY_ARRAY;
First : MY_ARRAY;
Second : MY_ARRAY;
Funny : array(1..5) of INTEGER;
X,Y : NEW_ARRAY_TYPE;
Fourth_Value : INTEGER renames First(4);
begin
for Index in 12..27 loop
X(Index) := Index + 7;
end loop;
Y := X; -- These are now assignment compatible
First(1) := 12;
First(2) := 16;
First(3) := First(2) - First(1);
Fourth_Value := -13;
First(5) := 16 - 2*First(2);
for Index in 1..5 loop
Second(Index) := 3 * Index + 77;
end loop;
Total := First;
if Total = First then
Put("Both arrays are the same size and contain ");
Put_Line("the same values in all elements.");
end if;
for Index in 1..5 loop
Total(Index) := Total(Index) + Second(Index);
Funny(Index) := Total(Index) + First(6 - Index);
Put("The array values are");
Put(Total(Index), 6);
Put(Funny(Index), 6);
New_Line;
end loop;
end CH10_1;
-- Result of execution
-- Both arrays are the same size and contain the same values in...
-- The array values are 92 76
-- The array values are 99 86
-- The array values are 90 94
-- The array values are 76 92
-- The array values are 76 88
-- Chapter 10 - Programming exercise 2
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Ch10_2 is
type MATRIX is array(1..3,1..5) of INTEGER;
First : MATRIX := ((1, 1, 1, 1, 1),
(2, 2, 2, 2, 2),
(3, 3, 3, 3, 3));
Second : MATRIX := ((1, 2, 3, 4, 5),
(1, 2, 3, 4, 5),
(1, 2, 3, 4, 5));
Result : MATRIX;
begin
for Index1 in 1..3 loop
for Index2 in 1..5 loop
Result(Index1, Index2) := First(Index1, Index2) *
Second(Index1, Index2);
end loop;
end loop;
for Index1 in 1..3 loop
for Index2 in 1..5 loop
Put(Result(Index1, Index2), 4);
end loop;
New_Line;
end loop;
end Ch10_2;
-- Result of execution
-- 1 2 3 4 5
-- 2 4 6 8 10
-- 3 6 9 12 15
-- Chapter 11 - Program 1
with Ada.Text_IO;
use Ada.Text_IO;
procedure Chars is
My_Char : CHARACTER;
Another : CHARACTER := 'D';
begin
My_Char := 'A';
if My_Char < Another then
Put_Line("My_Char is less than Another.");
end if;
Put(My_Char);
Put(Another);
Put(My_Char);
Put_Line(" character output.");
My_Char := CHARACTER'SUCC(My_Char); -- 'B'
My_Char := CHARACTER'FIRST; -- nul code
My_Char := CHARACTER'LAST; -- del code
end Chars;
-- Result of execution
-- My_Char is less than Another.
-- ADA character output.
-- Chapter 11 - Program 2
with Ada.Text_IO;
use Ada.Text_IO;
procedure String1 is
Line : STRING(1..33);
NAME : constant STRING := ('J','o','h','n');
JOB : constant STRING := "Computer Programmer";
Address : STRING(1..13) := "Anywhere, USA";
Letter : CHARACTER;
EXAMPLE1 : constant STRING := "A"; -- A string of length 1
EXAMPLE2 : constant STRING := ""; -- An empty string
begin
Line := "This is a test of STRINGS in Ada.";
Put(Line);
New_Line;
Put(NAME);
Put(" is a ");
Put(JOB);
Put(" and lives in ");
Put(Address);
New_Line(2);
Address(3) := 'X'; -- Individual letters
Address(4) := 'Y'; -- Individual letters
Address(10..13) := NAME(1..4); -- A slice
Put(Address);
New_Line;
end String1;
-- Result of execution
-- This is a test of STRINGS in Ada.
-- John is a Computer Programmer and lives in Anywhere, USA
--
-- AnXYhere,John
-- Chapter 11 - Program 3
with Ada.Text_IO, Ada.Characters.Latin_1;
use Ada.Text_IO;
procedure Concat is
String4 : STRING(1..4);
String7 : STRING(1..7);
begin
String7 := "CAT" & "FISH"; -- CATFISH
Put(String7); New_Line;
String4 := "CAT" & "S"; -- CATS
Put(String4); New_Line;
String4 := "S" & "CAT"; -- SCAT
Put(String4); New_Line;
String7 := String4 & "cat"; -- SCATcat
Put(String7); New_Line;
String7 := "Go" & Ada.Characters.Latin_1.CR &
Ada.Characters.Latin_1.LF & "Car"; -- Go
Put(String7); New_Line; -- Car
String7(3..5) := "ldb"; -- Goldbar
Put(String7); New_Line;
end Concat;
-- Result of execution
-- CATFISH
-- CATS
-- SCAT
-- SCATcat
-- Go
-- Car
-- Goldbar
-- Chapter 11 - Program 4
with Ada.Text_IO;
use Ada.Text_IO;
procedure StrngCmp is
MY_CAR : constant STRING := "Ford";
YOUR_CAR : constant STRING := "FORD";
Her_Car : STRING(1..8) := "Mercedes";
Rental : STRING(1..4);
Lease : STRING(1..4);
begin
if MY_CAR /= YOUR_CAR then
Put_Line("Case matters in a STRING constant or variable");
end if;
Her_Car := "Ford "; -- This is still not equal to My_Car
Rental := MY_CAR;
Rental := "Junk";
Lease := Rental;
If Rental = "Junk" then
Put_Line("A variable can be compared to a string literal.");
end if;
end StrngCmp;
-- Result of execution
-- Case matters in a STRING constant or variable
-- A variable can be compared to a string literal.
-- Chapter 11 - Program 5
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure CharInt is
Char : CHARACTER;
Index : INTEGER;
Stuff : array(0..25) of CHARACTER;
begin
Char := 'A';
Index := 5 + CHARACTER'POS(Char);
Put(Index, 5);
Char := CHARACTER'VAL(Index);
Put(Char);
New_Line;
Stuff(21) := 'X';
Index := 2 + CHARACTER'POS(Stuff(21));
Put(Index, 5);
Stuff(0) := CHARACTER'VAL(Index);
Put(Stuff(0));
end CharInt;
-- Result of execution
-- 70F
-- 90Z
-- Chapter 11 - Programming exercise 1
with Ada.Text_IO;
use Ada.Text_IO;
procedure Ch11_1 is
First_Name : constant STRING := "John";
Last_Name : constant STRING := "Doe";
Full_Name : STRING(1..8);
begin
Full_Name := First_Name & ' ' & Last_Name;
Put_Line(Full_Name);
end Ch11_1;
-- Result of execution
-- John Doe
-- Chapter 11 - Programming exercise 2
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure CH11_2 is
Char : CHARACTER;
Index : INTEGER;
Stuff : array(0..25) of CHARACTER;
begin
Char := 'A';
Index := 5 + CHARACTER'POS(Char);
Put(Index, 7);
Char := CHARACTER'VAL(Index);
Put(Char);
New_Line;
Stuff(21) := 'X';
Index := 2 + CHARACTER'POS(Stuff(21));
Put(Index, 7);
Stuff(0) := CHARACTER'VAL(Index);
Put(Stuff(0));
New_Line;
Char := CHARACTER'VAL(CHARACTER'POS(Char) + 1);
Put(Char);
Char := CHARACTER'SUCC(Char);
Put(Char);
end CH11_2;
-- Result of execution
-- 70F
-- 90Z
-- GH
-- Chapter 12 - Program 1
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Record1 is
type DATE is
record
Month : INTEGER range 1..12;
Day : INTEGER range 1..31;
Year : INTEGER range 1776..2010;
end record;
Independence_Day : DATE;
Birth_Day : DATE;
Today,Pay_Day : DATE := (5, 25, 1982);
begin
Independence_Day.Month := 7;
Independence_Day.Day := 4;
Independence_Day.Year := 1776;
Birth_Day := Independence_Day;
Pay_Day.Day := 30;
Put("Independence day was on ");
Put(Independence_Day.Month, 2);
Put("/");
Put(Independence_Day.Day, 2);
Put("/");
Put(Independence_Day.Year, 4);
New_Line;
Birth_Day := (Day => 19, Month => 2, Year => 1937);
Today := (7, 14, 1952);
Pay_Day := (7, Year => 1954, Day => 17);
end Record1;
-- Result of execution
-- Independence day was on 7/ 4/1776
-- Chapter 12 - Program 2
with Ada.Text_IO;
use Ada.Text_IO;
procedure Record2 is
type DATE is
record
Month : INTEGER range 1..12;
Day : INTEGER range 1..31;
Year : INTEGER range 1776..2010;
end record;
type PERSON is
record
Name : STRING(1..15);
Birth_Day : DATE;
Age : INTEGER;
Sex : CHARACTER;
end record;
Self, Mother, Father : PERSON;
My_Birth_Year : INTEGER renames Self.Birth_Day.Year;
begin
Self.Name := "John Q. Doe ";
Self.Age := 21;
Self.Sex := 'M';
Self.Birth_Day.Month := 10;
Self.Birth_Day.Day := 18;
Self.Birth_Day.Year := 1938;
My_Birth_Year := 1938; -- Identical to previous statement
Mother := Self;
Father.Birth_Day := Mother.Birth_Day;
Father.Birth_Day.Month := Self.Birth_Day.Month - 4;
Mother.Sex := 'F';
if Mother /= Self then
Put_Line("Mother is not equal to Self.");
end if;
end Record2;
-- Result of execution
-- Mother is not equal to Self.
-- Chapter 12 - Program 3
with Ada.Text_IO;
use Ada.Text_IO;
procedure Record3 is
type MONTH_NAME is (JAN,FEB,MAR,APR,MAY,JUN,JUL,
AUG,SEP,OCT,NOV,DEC);
type DATE is
record
Month : MONTH_NAME;
Day : INTEGER range 1..31;
Year : INTEGER range 1776..2010;
end record;
type GRADE_ARRAY is array(1..4) of POSITIVE;
type PERSON is
record
Name : STRING(1..15);
Birth_Day : DATE;
Graduation_Day : DATE := (MAY,27,1987);
Age : INTEGER := 21;
Sex : CHARACTER := 'F';
Grades : GRADE_ARRAY;
end record;
Self, Mother, Father : PERSON;
begin
Self.Name := "John Q. Doe ";
Self.Sex := 'M';
Self.Birth_Day.Month := OCT;
Self.Birth_Day.Day := 18;
Self.Birth_Day.Year := 1938;
Self.Grades(1) := 85;
Self.Grades(2) := 90;
Self.Grades(3) := 75;
Self.Grades(4) := 92;
Mother := Self;
Father.Birth_Day := Mother.Birth_Day;
Father.Birth_Day.Day := Self.Birth_Day.Day - 4;
Mother.Sex := 'F';
end Record3;
-- Result of execution
-- (No output from this program.)
-- Chapter 12 - Program 4
with Ada.Text_IO;
use Ada.Text_IO;
procedure Record4 is
type MONTH_NAME is (JAN,FEB,MAR,APR,MAY,JUN,JUL,
AUG,SEP,OCT,NOV,DEC);
type DATE is
record
Month : MONTH_NAME;
Day : INTEGER range 1..31;
Year : INTEGER range 1776..2010;
end record;
type PERSON is
record
Name : STRING(1..15);
Birth_Day : DATE;
Age : INTEGER := 15;
Sex : CHARACTER := 'M';
end record;
Teacher : PERSON;
Class_Member : array(1..35) of PERSON;
Standard : constant PERSON := ("John Q. Doe ",
(MAR, 27, 1955), 33, 'M');
type EMPTY_RECORD is
record
null;
end record;
type ANOTHER_EMPTY_RECORD is null record;
begin
Teacher.Name := "John Q. Doe ";
Teacher.Age := 21;
Teacher.Sex := 'M';
Teacher.Birth_Day.Month := OCT;
Teacher.Birth_Day.Day := 18;
Teacher.Birth_Day.Year := 1938;
for Index in Class_Member'RANGE loop
Class_Member(Index).Name := "Suzie Lou Q ";
Class_Member(Index).Birth_Day.Month := MAY;
Class_Member(Index).Birth_Day.Day := 23;
Class_Member(Index).Birth_Day.Year := 1956;
Class_Member(Index).Sex := 'F';
end loop;
Class_Member(4).Name := "Little Johhny ";
Class_Member(4).Sex := 'M';
Class_Member(4).Birth_Day.Day := 17;
Class_Member(7).Age := 14;
Class_Member(2) := Standard;
Class_Member(3) := Standard;
end Record4;
-- Result of execution
-- (No output from this program.)
-- Chapter 12 - Programming exercise 1
with Ada.Text_IO;
use Ada.Text_IO;
procedure CH12_1 is
type MONTH_NAME is (JAN,FEB,MAR,APR,MAY,JUN,JUL,
AUG,SEP,OCT,NOV,DEC);
type DATE is
record
Month : MONTH_NAME;
Day : INTEGER range 1..31;
Year : INTEGER range 1776..2010;
end record;
type PERSON is
record
Name : STRING(1..15);
Birth_Day : DATE;
Age : INTEGER := 15;
Sex : CHARACTER := 'M';
end record;
Teacher : PERSON;
Class_Member : array(1..35) of PERSON;
Standard : constant PERSON :=
(Birth_Day => (Month => MAR,
Year => 1955,
Day => 27),
Name => "John Q. Doe ",
Age => 33,
Sex => 'M');
type EMPTY_RECORD is
record
null;
end record;
begin
Teacher.Name := "John Q. Doe ";
Teacher.Age := 21;
Teacher.Sex := 'M';
Teacher.Birth_Day.Month := OCT;
Teacher.Birth_Day.Day := 18;
Teacher.Birth_Day.Year := 1938;
for Index in Class_Member'RANGE loop
Class_Member(Index).Name := "Suzie Lou Q ";
Class_Member(Index).Birth_Day.Month := MAY;
Class_Member(Index).Birth_Day.Day := 23;
Class_Member(Index).Birth_Day.Year := 1956;
Class_Member(Index).Sex := 'F';
end loop;
Class_Member(4).Name := "Little Johhny ";
Class_Member(4).Sex := 'M';
Class_Member(4).Birth_Day.Day := 17;
Class_Member(7).Age := 14;
Class_Member(2) := Standard;
Class_Member(3) := Standard;
end CH12_1;
-- Result of execution
-- (No output from this program.)
-- Chapter 12 - Program 1
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure CH12_2 is
type MONTH_NAME is (JAN,FEB,MAR,APR,MAY,JUN,JUL,
AUG,SEP,OCT,NOV,DEC);
package Enum_IO is new Ada.Text_IO.Enumeration_IO(MONTH_NAME);
use Enum_IO;
type DATE is
record
Month : MONTH_NAME;
Day : INTEGER range 1..31;
Year : INTEGER range 1776..2010;
end record;
Independence_Day : DATE;
Birth_Day : DATE;
Today,Pay_Day : DATE := (MAY,25,1982);
begin
Independence_Day.Month := JUL;
Independence_Day.Day := 4;
Independence_Day.Year := 1776;
Birth_Day := Independence_Day;
Pay_Day.Day := 30;
Put("Independence day was on ");
Put(Independence_Day.Month, 2);
Put(Independence_Day.Day, 2);
Put(",");
Put(Independence_Day.Year, 5);
New_Line;
Birth_Day := (Day => 19, Month => FEB, Year => 1937);
Today := (JUL, 14, 1952);
Pay_Day := (JUL, Year => 1954, Day => 17);
end CH12_2;
-- Result of execution
-- Independence day was on JUL 4, 1776
-- Chapter 13 - Program 1
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Access1 is
type POINT_SOMEWHERE is access INTEGER;
Index, Arrow, There : POINT_SOMEWHERE;
begin
Index := new INTEGER;
Index.all := 13;
Put("The value is");
Put(Index.all, 6);
New_Line;
Arrow := new INTEGER;
Arrow.all := Index.all + 16;
There := Arrow;
Put("The values are now");
Put(Index.all, 6);
Put(Arrow.all, 6);
Put(There.all, 6);
New_Line;
There.all := 21;
Put("The values are now");
Put(Index.all, 6);
Put(Arrow.all, 6);
Put(There.all, 6);
New_Line;
end Access1;
-- Result of execution
-- The value is 13
-- The values are now 13 29 29
-- The values are now 13 21 21
-- Chapter 13 - Program 2
with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO;
procedure Access2 is
type POINT_TO_INT is access INTEGER;
Index, Arrow : POINT_TO_INT;
type POINT_TO_FLOAT is access FLOAT;
X, Y, Z : POINT_TO_FLOAT;
begin
Index := new INTEGER'(173);
Arrow := new INTEGER'(57);
Put("The values are");
Put(Index.all, 6);
Put(Arrow.all, 6);
New_Line;
Index.all := 13;
Arrow.all := Index.all;
Index := Arrow;
X := new FLOAT'(3.14159);
Y := X;
Z := X;
Put("The float values are");
Put(X.all, 6, 6, 0);
Put(Y.all, 6, 6, 0);
Put(Z.all, 6, 6, 0);
New_Line;
X.all := 2.0 * Y.all;
Put("The float values are");
Put(X.all, 6, 6, 0);
Put(Y.all, 6, 6, 0);
Put(Z.all, 6, 6, 0);
New_Line;
end Access2;
-- Result of execution
-- The values are 173 57
-- The float values are 3.141590 3.141590 3.141590
-- The float values are 6.283180 6.283180 6.283180
-- Chapter 13 - Program 3
with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Unchecked_Deallocation;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Access3 is
type MY_RECORD is
record
Age : INTEGER;
Initial : CHARACTER;
Sex : CHARACTER;
end record;
type ACCESS_MY_DATA is access MY_RECORD;
procedure Free is new
Ada.Unchecked_Deallocation(MY_RECORD, ACCESS_MY_DATA);
Myself : ACCESS_MY_DATA;
Friend : ACCESS_MY_DATA := new MY_RECORD'(30, 'R', 'F');
Result : BOOLEAN;
begin
Myself := new MY_RECORD;
Myself.Age := 34;
Myself.Initial := 'D';
Myself.Sex := 'M';
Friend := new MY_RECORD'(31, 'R', 'F');
Put("My age is");
Put(Myself.Age, 3);
Put(" and my initial is ");
Put(Myself.Initial);
New_Line;
Friend.all := Myself.all;
Result := Friend.all = Myself.all; -- TRUE because of line 43
Result := Friend = Myself; -- FALSE because they point
-- to different things.
Free(Myself);
Free(Friend);
end Access3;
-- Result of execution
-- My age is 34 and my initial is D
-- Chapter 13 - Program 4
with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Unchecked_Deallocation;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Access4 is
type MY_ARRAY is array(3..8) of INTEGER;
type POINT_TO_ARRAY is access MY_ARRAY;
procedure Free is new
Ada.Unchecked_Deallocation(MY_ARRAY, POINT_TO_ARRAY);
List_Of_Stuff : MY_ARRAY := (34, 12, -14, 1, 27, -11);
There : POINT_TO_ARRAY;
Here : POINT_TO_ARRAY;
begin
There := new MY_ARRAY;
There.all := List_Of_Stuff;
Here := There;
for Index in MY_ARRAY'RANGE loop
Put(List_Of_Stuff(Index), 6);
Put(There.all(Index), 6);
Put(Here.all(Index), 6);
New_Line;
end loop;
Free(There);
end Access4;
-- Result of execution
-- 34 34 34
-- 12 12 12
-- -14 -14 -14
-- 1 1 1
-- 27 27 27
-- -11 -11 -11
-- Chapter 13 - Program 5
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Access5 is
type MY_RECORD is
record
Age : INTEGER;
Initial : CHARACTER;
Sex : CHARACTER;
end record;
type ACCESS_MY_DATA is access MY_RECORD;
Myself : ACCESS_MY_DATA;
Class : array(1..10) of ACCESS_MY_DATA;
begin
Myself := new MY_RECORD;
Myself.Age := 34;
Myself.Initial := 'D';
Myself.Sex := 'M';
for Index in 1..10 loop
Class(Index) := new MY_RECORD;
Class(Index).all := Myself.all;
end loop;
Class(3).Age := 30;
Class(3).Initial := 'A';
Class(5).Initial := 'Z';
Class(8).Initial := 'R';
Class(6).Sex := 'F';
Class(7).Sex := 'F';
Class(2).Sex := 'F';
for Index in 1..10 loop
Put("The class members age is");
Put(Class(Index).Age, 3);
if Class(Index).Sex = 'M' then
Put(" and his initial is ");
else
Put(" and her initial is ");
end if;
Put(Class(Index).Initial);
New_Line;
end loop;
end Access5;
-- Result of execution
-- The class members age is 34 and his initial is D
-- The class members age is 34 and her initial is D
-- The class members age is 30 and his initial is A
-- The class members age is 34 and his initial is D
-- The class members age is 34 and his initial is Z
-- The class members age is 34 and her initial is D
-- The class members age is 34 and her initial is D
-- The class members age is 34 and his initial is R
-- The class members age is 34 and his initial is D
-- The class members age is 34 and his initial is D
-- Chapter 13 - Program 6
with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO;
procedure AcessAll is
type BRICK_TYPE is record
Length : INTEGER;
Width : INTEGER;
Height : INTEGER;
end record;
type ACCESS_INT is access all INTEGER;
type ACCESS_FLT is access all FLOAT;
type ACCESS_BRICK is access all BRICK_TYPE;
Number : INTEGER := 27;
Count : aliased INTEGER := 12;
Pt_Count : ACCESS_INT;
Size : aliased FLOAT := 7.4;
Pt_Size : ACCESS_FLT;
Brick : aliased BRICK_TYPE := (3, 4, 6);
Pt_Brick : ACCESS_BRICK;
begin
Pt_Count := Count'Access;
-- Pt_Count := Number'Access; Illegal - Number not aliased
-- Pt_Count := Size'Access; Illegal - Size is FLOAT
-- Pt_Count := Brick'Access; Illegal - Brick is BRICK_TYPE
Pt_Count.All := Pt_Count.All + 13;
Put("The value of Count is now ");
Put(Count, 5);
New_Line;
Pt_Size := Size'Access;
Put("The value of Size is now ");
Put(Pt_Size.All, 5, 2, 0);
New_Line;
Pt_Brick := Brick'Access;
Put("The brick measures ");
Put(Pt_Brick.Length, 5); -- Using the pointer
Put(Pt_Brick.Width, 5); -- Using the pointer
Put(Brick.Height, 5); -- Using the object
New_Line;
end AcessAll;
-- Result of execution
--
-- The value of Count is now 25
-- The value of Size is now 7.40
-- The brick measures 3 4 6
-- Chapter 13 - Program 7
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure AcessFnc is
function Double_It(In_Value : INTEGER) return INTEGER is
begin
return 2 * In_Value;
end Double_It;
function Triple_It(In_Value : INTEGER) return INTEGER is
begin
return 3 * In_Value;
end Triple_It;
function Decade_It(Number : INTEGER) return INTEGER is
begin
return 10 * Number;
end Decade_It;
type ACCESS_FUNCTION is access function(X : INTEGER) return INTEGER;
Multiply : ACCESS_FUNCTION;
Worker : INTEGER := 17;
begin
Multiply := Double_It'Access;
Put("Double value is");
Put(Multiply(Worker), 6);
New_Line;

Multiply := Decade_It'Access;
Put("Ten times value is");
Put(Multiply(Worker), 6);
New_Line;

Multiply := Triple_It'Access;
Put("Triple value is");
Put(Multiply(Worker), 6);
New_Line;

end AcessFnc;
-- Result of execution
--
-- Double value is 34
-- Ten times value is 170
-- Triple value is 51
-- Chapter 13 - Programming exercise 1
with Ada.Text_IO, Ada.Float_Text_IO;
use Ada.Text_IO, Ada.Float_Text_IO;
procedure Ch13_1 is
type BOX_TYPE is
record
Length : FLOAT;
Width : FLOAT;
Height : FLOAT;
end record;
type POINT_TO_BOX is access BOX_TYPE;
Small, Medium, Large : POINT_TO_BOX;
begin
Small := new BOX_TYPE;
Medium := new BOX_TYPE;
Large := new BOX_TYPE;
Small.Length := 3.5;
Small.Width := 2.4;
Small.Height := 1.9;
Medium.Length := 7.4;
Medium.Width := 6.4;
Medium.Height := 9.3;
Large.Length := 13.8;
Large.Width := 21.5;
Large.Height := 15.1;
Put(Small.Length, 8, 3, 0);
Put(Small.Width, 8, 3, 0);
Put(Small.Height, 8, 3, 0);
Put(Small.Length * Small.Width * Small.Height, 8, 3, 0);
New_Line;
Put(Medium.Length, 8, 3, 0);
Put(Medium.Width, 8, 3, 0);
Put(Medium.Height, 8, 3, 0);
Put(Medium.Length * Medium.Width * Medium.Height, 8, 3, 0);
New_Line;
Put(Large.Length, 8, 3, 0);
Put(Large.Width, 8, 3, 0);
Put(Large.Height, 8, 3, 0);
Put(Large.Length * Large.Width * Large.Height, 8, 3, 0);
New_Line;
end Ch13_1;
-- Result of execution
-- 3.500 2.400 1.900 15.960
-- 7.400 6.400 9.300 440.448
-- 13.800 21.500 15.100 4480.170
-- Chapter 13 - Programming Exercise 2
-- Chapter 13 - Program 1
with Ada.Text_IO, Ada.Integer_Text_IO, Unchecked_Deallocation;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Ch13_2a is
type POINT_SOMEWHERE is access INTEGER;
Index,Arrow,There : POINT_SOMEWHERE;
procedure Free is new
Unchecked_Deallocation(INTEGER, POINT_SOMEWHERE);
begin
Index := new INTEGER;
Index.all := 13;
Put("The value is");
Put(Index.all, 5);
New_Line;
Arrow := new INTEGER;
Arrow.all := Index.all + 16;
There := Arrow;
Put("The values are now");
Put(Index.all, 5);
Put(Arrow.all, 5);
Put(There.all, 5);
New_Line;
There.all := 21;
Put("The values are now");
Put(Index.all, 5);
Put(Arrow.all, 5);
Put(There.all, 5);
New_Line;
Free(Index);
Free(Arrow);
end Ch13_2a;
-- Result of execution
-- The value is 13
-- The values are now 13 29 29
-- The values are now 13 21 21
-- Note that the Free procedure in line 41 could have been done
-- with the access variable There since it is also accessing
-- that data. Both could not be used however.
-- Chapter 13 - Programming Exercise 2
-- Chapter 13 - Program 2
with Ada.Text_IO, Unchecked_Deallocation;
use Ada.Text_IO;
procedure Ch13_2b is
package Int_IO is new Ada.Text_IO.Integer_IO(INTEGER);
use Int_IO;
package Flt_IO is new Ada.Text_IO.Float_IO(FLOAT);
use Flt_IO;
type POINT_TO_INT is access INTEGER;
Index,Arrow : POINT_TO_INT;
type POINT_TO_FLOAT is access FLOAT;
X,Y,Z : POINT_TO_FLOAT;
procedure Free is new
Unchecked_Deallocation(INTEGER, POINT_TO_INT);
procedure Free is new
Unchecked_Deallocation(FLOAT,POINT_TO_FLOAT);
begin
Index := new INTEGER'(173);
Arrow := new INTEGER'(57);
Put("The values are");
Put(Index.all, 6);
Put(Arrow.all, 6);
New_Line;
Index.all := 13;
Arrow.all := Index.all;
Index := Arrow;
X := new FLOAT'(3.14159);
Y := X;
Z := X;
Put("The float values are");
Put(X.all, 6, 6, 0);
Put(Y.all, 6, 6, 0);
Put(Z.all, 6, 6, 0);
New_Line;
X.all := 2.0 * Y.all;
Put("The float values are");
Put(X.all, 6, 6, 0);
Put(Y.all, 6, 6, 0);
Put(Z.all, 6, 6, 0);
New_Line;
Free(Index);
Free(X);
end Ch13_2b;
-- Result of execution
-- The values are 173 57
-- The float values are 3.141590 3.141590 3.141590
-- The float values are 6.283180 6.283180 6.283180
-- Note that the deallocations could be done with any of the
-- variable names since all variables of the same type are point-
-- ing to the same places.
-- Note also that the two procedures could be named something else
-- rather than overloading the name Free. It would be better
-- practice to use different names for the variables.
-- Chapter 13 - Programming Exercise 2
-- Chapter 13 - Program 5
with Ada.Text_IO, Ada.Integer_Text_IO, Unchecked_Deallocation;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Ch13_2c is
type MY_RECORD is
record
Age : INTEGER;
Initial : CHARACTER;
Sex : CHARACTER;
end record;
type ACCESS_MY_DATA is access MY_RECORD;
procedure Free is new
Unchecked_Deallocation(MY_RECORD,ACCESS_MY_DATA);
Myself : ACCESS_MY_DATA;
Class : array(1..10) of ACCESS_MY_DATA;
begin
Myself := new MY_RECORD;
Myself.Age := 34;
Myself.Initial := 'D';
Myself.Sex := 'M';
for Index in 1..10 loop
Class(Index) := new MY_RECORD;
Class(Index).all := Myself.all;
end loop;
Class(3).Age := 30;
Class(3).Initial := 'A';
Class(5).Initial := 'Z';
Class(8).Initial := 'R';
Class(6).Sex := 'F';
Class(7).Sex := 'F';
Class(2).Sex := 'F';
for Index in 1..10 loop
Put("The class members age is");
Put(Class(Index).Age, 3);
if Class(Index).Sex = 'M' then
Put(" and his initial is ");
else
Put(" and her initial is ");
end if;
Put(Class(Index).Initial);
New_Line;
end loop;
for Index in 1..10 loop
Free(Class(Index));
end loop;
Free(Myself);
end Ch13_2c;
-- Result of execution
-- The class members age is 34 and his initial is D
-- The class members age is 34 and her initial is D
-- The class members age is 30 and his initial is A
-- The class members age is 34 and his initial is D
-- The class members age is 34 and his initial is Z
-- The class members age is 34 and her initial is D
-- The class members age is 34 and her initial is D
-- The class members age is 34 and his initial is R
-- The class members age is 34 and his initial is D
-- The class members age is 34 and his initial is D
-- Chapter 14 - Program 1
with Ada.Text_IO;
use Ada.Text_IO;
procedure Formats is
type MY_FIXED is delta 0.01 range 20.0..42.0;
type DAY is (MON, TUE, WED, THU, FRI, SAT, SUN);
type MY_INTEGER is range -13..323;
X_Value : FLOAT := 3.14;
Index : INTEGER := 27;
Count : MY_INTEGER := -7;
What : BOOLEAN := TRUE;
Who : BOOLEAN := FALSE;
Size : MY_FIXED := 24.33;
Today : DAY := TUE;
package Int_IO is new Ada.Text_IO.Integer_IO(INTEGER);
use Int_IO;
package Flt_IO is new Ada.Text_IO.Float_IO(FLOAT);
use Flt_IO;
package Enum_IO is new Ada.Text_IO.Enumeration_IO(BOOLEAN);
use Enum_IO;
package Fix_IO is new Ada.Text_IO.Fixed_IO(MY_FIXED);
use Fix_IO;
package Day_IO is new Ada.Text_IO.Enumeration_IO(DAY);
use Day_IO;
package New_Int_IO is new Ada.Text_IO.Integer_IO(MY_INTEGER);
use New_Int_IO;
begin
-- INTEGER outputs
Put("Index is --->"); Put(Index); Put("<---"); New_Line;
Put("Index is --->"); Put(Index,3); Put("<---"); New_Line;
Put("Index is --->"); Put(Index,8); Put("<---"); New_Line(2);
Put("Count is --->"); Put(Count); Put("<---"); New_Line;
Put("Count is --->"); Put(Count,3); Put("<---"); New_Line;
Put("Count is --->"); Put(Count,8); Put("<---"); New_Line(2);
-- FLOAT outputs
Put("Put(X_Value) -------->"); Put(X_Value); New_Line;
Put("Put(X_Value,5) ------>"); Put(X_Value,5); New_Line;
Put("Put(X_Value,5,5) ---->"); Put(X_Value,5,5); New_Line;
Put("Put(X_Value,5,5,0) -->"); Put(X_Value,5,5,0); New_Line(2);
-- MY_FIXED outputs
Put("Put(Size) -------->"); Put(Size); New_Line;
Put("Put(Size,5) ------>"); Put(Size,5); New_Line;
Put("Put(Size,5,5) ---->"); Put(Size,5,5); New_Line;
Put("Put(Size,5,5,0) -->"); Put(Size,5,5,0); New_Line(2);
-- BOOLEAN outputs
Put("What is ---->"); Put(What); Put("<---"); New_Line;
Put("Who is ----->"); Put(Who); Put("<---"); New_Line;
Put("What is ---->"); Put(What,7); Put("<---"); New_Line;
Put("Who is ----->"); Put(Who,8); Put("<---"); New_Line;
Put("TRUE is ---->"); Put(TRUE); Put("<---"); New_Line;
Put("FALSE is --->"); Put(FALSE); Put("<---"); New_Line(2);
-- Enumeration outputs
Put("Today is --->"); Put(Today); Put("<---"); New_Line;
Put("Today is --->"); Put(Today,6); Put("<---"); New_Line;
Put("Today is --->"); Put(Today,7); Put("<---"); New_Line;
Put("WED is ----->"); Put(WED); Put("<---"); New_Line;
Put("WED is ----->"); Put(WED,5); Put("<---"); New_Line(2);
end Formats;
-- Result of execution
-- Index is ---> 27<---
-- Index is ---> 27<---
-- Index is ---> 27<---
--
-- Count is ---> -7<---
-- Count is ---> -7<---
-- Count is ---> -7<---
--
-- Put(X_Value) --------> 3.14000E+00
-- Put(X_Value,5) ------> 3.14000E+00
-- Put(X_Value,5,5) ----> 3.14000E+00
-- Put(X_Value,5,5,0) --> 3.14000
--
-- Put(Size) --------> 24.33
-- Put(Size,5) ------> 24.33
-- Put(Size,5,5) ----> 24.33008
-- Put(Size,5,5,0) --> 24.33008
--
-- What is ---->TRUE<---
-- Who is ----->FALSE<---
-- What is ---->TRUE <---
-- Who is ----->FALSE <---
-- TRUE is ---->TRUE<---
-- FALSE is --->FALSE<---
--
-- Today is --->TUE<---
-- Today is --->TUE <---
-- Today is --->TUE <---
-- WED is ----->WED<---
-- WED is ----->WED <---
-- Chapter 14 - Program 2
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure EasyOut is
Turkey : FILE_TYPE;
begin
-- First we create the file
Create(Turkey, Out_File, "TEST.TXT");
-- Then we write to it
Put_Line(Turkey, "This is a test of turkey");
Put(Turkey, "and it should work well.");
New_Line(Turkey, 2);
Put_Line("Half of the turkey test");
Set_Output(Turkey); -- Make Turkey the default output
Put_Line("This is another test of turkey");
Put("and it should work well.");
New_Line(2);
Put_Line(Standard_Output, "Half of the turkey test");
Set_Output(Standard_Output); -- Return to the Standard default
Put_Line("Back to the standard default output.");
Close(Turkey);
end EasyOut;
-- Result of execution
-- (Output to the monitor follows)
--
-- Half of the turkey test
-- Half of the turkey test
-- Back to the standard default output
-- (Output to the file TEST.TXT)
--
-- This is a test of turkey
-- and it should work well.
--
-- This is another test of turkey
-- and it should work well.
-- Chapter 14 - Program 3
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure MultOut is
Numbers, Some_Text, More_Text : FILE_TYPE;
Index : INTEGER;
begin
Create(Numbers, Out_File, "INTDATA.TXT");
Create(Some_Text, Out_File, "TEST.TXT");
Create(More_Text, Out_File, "CHARACTS.TXT");
for Count in 3..5 loop
Put(Some_Text, "This line goes to the TEST.TXT file.");
Put(More_Text, "This line goes to the CHARACTS.TXT file.");
for Count2 in 12..16 loop
Put(Numbers, Count + Count2);
end loop;
New_Line(More_Text);
New_Line(Numbers, 2);
New_Line(Some_Text);
end loop;
Put_Line("INTDATA.TXT, TEST.TXT, and CHARACTS.TXT are ready.");
Close(Numbers);
Close(Some_Text);
Close(More_Text);
end MultOut;
-- Results of execution
-- (Output to the monitor)
--
-- INTDATA.TXT, TEST.TXT, and CHARACTS.TXT are ready.
-- (Output to the file named INTDATA.TXT)
--
-- 15 16 17 18 19
--
-- 16 17 18 19 20
--
-- 17 18 19 20 21
--
-- (Output to the file named TEST.TXT)
--
-- This line goes to the TEST.TXT file.
-- This line goes to the TEST.TXT file.
-- This line goes to the TEST.TXT file.
-- Output to the file named CHARACTS.TXT)
--
-- This line goes to the CHARACTS.TXT file.
-- This line goes to the CHARACTS.TXT file.
-- This line goes to the CHARACTS.TXT file.
-- Chapter 14 - Program 4
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure CharIn is
My_File : FILE_TYPE;
One_Char : CHARACTER;
begin
open(My_File, In_File, "CHARACTS.TXT");
loop -- Read one character at a time and display it
exit when End_Of_File(My_File);
Get(My_File, One_Char);
Put(One_Char);
end loop;
New_Line(2);
Reset(My_File); -- Reset and start over with the same file
loop -- Read and display but search for End of lines
exit when End_Of_File(My_File);
Get(My_File, One_Char);
if End_Of_Line(My_File) then
Put("<--- End of line found");
New_Line;
else
Put(One_Char);
end if;
end loop;
New_Line;
Reset(My_File); -- Reset and start over the third time
-- Read and display but search for End of lines
loop -- using a look ahead method
exit when End_Of_File(My_File);
Get(My_File, One_Char);
Put(One_Char);
if End_Of_Line(My_File) then
Put("<--- End of line found");
New_Line;
end if;
end loop;
Close(My_File);
end CharIn;
-- Result of execution
-- (Note; the first line is a full 80 columns wide.)
-- This line goes to the CHARACTS.TXT file.This line goes to...
-- This line goes to the CHARACTS.TXT file.
--
-- This line goes to the CHARACTS.TXT file<--- End of line found
-- This line goes to the CHARACTS.TXT file<--- End of line found
-- This line goes to the CHARACTS.TXT file<--- End of line found
--
-- This line goes to the CHARACTS.TXT file.<--- End of line found
-- This line goes to the CHARACTS.TXT file.<--- End of line found
-- This line goes to the CHARACTS.TXT file.<--- End of line found
-- Chapter 14 - Program 5
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure StringIn is
My_File : FILE_TYPE;
My_String : STRING(1..10);
begin
Open(My_File, In_File, "CHARACTS.TXT");
loop -- Read one string at a time and display it
exit when End_Of_File(My_File);
Get(My_File, My_String);
Put(My_String);
end loop;
New_Line(2);
Reset(My_File); -- Reset and start over with the same file
loop -- Read and display but search for End of lines
exit when End_Of_File(My_File);
Get(My_File, My_String);
Put(My_String);
if End_Of_Line(My_File) then
Put_Line("<--- End of line found");
else
Put_Line("<--- 10 characters");
end if;
end loop;
New_Line;
Close(My_File);
end StringIn;
-- Result of execution
-- (Note; The first line is a full 80 columns wide.)
-- This line goes to the CHARACTS.TXT file.This line goes to ...
-- This line goes to the CHARACTS.TXT file.
--
-- This line <--- 10 characters
-- goes to th<--- 10 characters
-- e CHARACTS<--- 10 characters
-- .TXT file.<--- End of line found
-- This line <--- 10 characters
-- goes to th<--- 10 characters
-- e CHARACTS<--- 10 characters
-- .TXT file.<--- End of line found
-- This line <--- 10 characters
-- goes to th<--- 10 characters
-- e CHARACTS<--- 10 characters
-- .TXT file.<--- End of line found
-- Chapter 14 - Program 6
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure IntIn is
Integer_File : FILE_TYPE;
Index : INTEGER;
begin
Open(Integer_File, In_File, "INTDATA.TXT");
while not End_Of_File(Integer_File) loop
if End_Of_Line(Integer_File) then
New_Line;
Skip_Line(Integer_File);
else
Get(Integer_File, Index);
Put("The value read in is");
Put(Index, 6);
New_Line;
end if;
end loop;
Close(Integer_File);
end IntIn;
-- Result of execution
-- The value read in is 15
-- The value read in is 16
-- The value read in is 17
-- The value read in is 18
-- The value read in is 19
--
-- The value read in is 16
-- The value read in is 17
-- The value read in is 18
-- The value read in is 19
-- The value read in is 20
--
-- The value read in is 17
-- The value read in is 18
-- The value read in is 19
-- The value read in is 20
-- The value read in is 21
-- Chapter 14 - Program 7
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure PrintOut is
Turkey : FILE_TYPE;
begin
-- First we create the file
Create(Turkey, Out_File, "LPT1");
-- Then we write to it
Put(Turkey, "This is a test of turkey");
New_Line(Turkey);
Put(Turkey, "and it should work well.");
New_Line(Turkey, 2);
Put("Half of the turkey test.");
New_Line;
Set_Output(Turkey); -- Make Turkey the default output
Put("This is another test of turkey");
New_Line;
Put("and it should work well.");
New_Line(2);
Put(Standard_Output, "Half of the turkey test.");
New_Line(Standard_Output);
Set_Output(Standard_Output); -- Return to the Standard default
Put("Back to the standard default output.");
Close(Turkey);
end PrintOut;
-- Results of execution
-- (Output to the monitor)
--
-- Half of the turkey test.
-- Half of the turkey test.
-- Back to the standard default output.
-- (Output to the printer)
--
-- This is a test of turkey
-- and it should work well.
--
-- This is another test of turkey
-- and it should work well.
--
-- Chapter 14 - Programming exercise 1
with Ada.Text_IO;
use Ada.Text_IO;
procedure CH14_1 is
One_Char : CHARACTER;
begin
Put_Line("Input characters to display, enter Q to stop.");
loop -- Read one character at a time and display it
Get(One_Char);
Put(One_Char);
New_Line;
exit when One_Char = 'Q';
end loop;
New_Line(2);
end CH14_1;
-- Result of execution
-- (The output depends on the input.)
-- Chapter 14 - Programming exercise 2
with Ada.Text_IO, Ada.Float_Text_IO;
use Ada.Text_IO, Ada.Float_Text_IO;
procedure Ch14_2 is
Float_File : FILE_TYPE;
Float_Dat : FLOAT;
begin
Open(Float_File, In_File, "FLTDATA.TXT");
while not End_Of_File(Float_File) loop
if End_Of_Line(Float_File) then
New_Line;
Skip_Line(Float_File);
else
Get(Float_File, Float_Dat);
Put("The value read in is");
Put(Float_Dat);
New_Line;
end if;
end loop;
Close(Float_File);
end Ch14_2;
-- Result of execution
-- (The output depemds on the input values.)
-- Chapter 15 - Program 1
-- Interface of AdderPkg
package AdderPkg is
type MY_ARRAY is array(INTEGER range <>) of FLOAT;
procedure Add_Em_Up(In_Dat : in MY_ARRAY;
Sum : out FLOAT);
end AdderPkg;
-- Implementation of AdderPkg
package body AdderPkg is
procedure Add_Em_Up(In_Dat : in MY_ARRAY;
Sum : out FLOAT) is
Total : FLOAT;
begin
Total := 0.0;
for Index in In_Dat'FIRST..In_Dat'LAST loop
Total := Total + In_Dat(Index);
end loop;
Sum := Total;
end Add_Em_Up;
end AdderPkg;
-- Result of execution
-- (This is not a stand alone package, so it has no output.)
-- Chapter 15 - Program 2
with Ada.Text_IO, Ada.Float_Text_IO;
use Ada.Text_IO, Ada.Float_Text_IO;
with AdderPkg;
use AdderPkg;
procedure Adder1 is
FIRST : constant := 2;
LAST : constant := 7;
Sum_Of_Values : FLOAT;
New_Array : MY_ARRAY(FIRST..LAST);
procedure Summer(In_Dat : MY_ARRAY;
Sum : out FLOAT) renames AdderPkg.Add_Em_Up;
begin
for Index in New_Array'FIRST..New_Array'LAST loop
New_Array(Index) := FLOAT(Index);
end loop;
Put_Line("Call Add_Em_Up now");
Add_Em_Up(New_Array, Sum_Of_Values);
Put("Back from Add_Em_Up, total is");
Put(Sum_Of_Values, 5, 2, 0);
New_Line;
-- The next three statements are identical
Add_Em_Up(New_Array, Sum_Of_Values);
AdderPkg.Add_Em_Up(New_Array, Sum_Of_Values);
Summer(New_Array, Sum_Of_Values);
end Adder1;
-- Result of execution
-- Call Add_Em_Up now
-- Back from Add_Em_Up, total is 27.00
-- Chapter 15 - Program 3
with Ada.Text_IO, Ada.Float_Text_IO;
use Ada.Text_IO, Ada.Float_Text_IO;
procedure Adder2 is
FIRST : constant := 2;
LAST : constant := 7;
Sum_Of_Values : FLOAT;
-- Interface of Package1
package AdderPkg is
type MY_ARRAY is array(INTEGER range <>) of FLOAT;
procedure Add_Em_Up(In_Dat : in MY_ARRAY;
Sum : out FLOAT);
end AdderPkg;
use Adder2.AdderPkg;
New_Array : MY_ARRAY(FIRST..LAST);
-- Implementation of Package1
package body AdderPkg is
procedure Add_Em_Up(In_Dat : in MY_ARRAY;
Sum : out FLOAT) is
Total : FLOAT;
begin
Total := 0.0;
for Index in In_Dat'FIRST..In_Dat'LAST loop
Total := Total + In_Dat(Index);
end loop;
Sum := Total;
end Add_Em_Up;
end AdderPkg;
begin
for Index in New_Array'FIRST..New_Array'LAST loop
New_Array(Index) := FLOAT(Index);
end loop;
Put_Line("Call Add_Em_Up now");
Add_Em_Up(New_Array, Sum_Of_Values);
Put("Back from Add_Em_Up, total is");
Put(Sum_Of_Values, 5, 2, 0);
New_Line;
end Adder2;
-- Result of execution
-- Call Add_Em_Up now
-- Back from Add_Em_Up, total is 27.00
-- Chapter 15 - Program 4
with Ada.Text_IO, Ada.Float_Text_IO;
use Ada.Text_IO, Ada.Float_Text_IO;
procedure Adder3 is
FIRST : constant := 2;
LAST : constant := 7;
Sum_Of_Values : FLOAT;
-- Interface of AdderStb
package AdderStb is
type MY_ARRAY is array(INTEGER range <>) of FLOAT;
procedure Add_Em_Up(In_Dat : in MY_ARRAY;
Sum : out FLOAT);
end AdderStb;
use AdderStb;
New_Array : MY_ARRAY(FIRST..LAST);
-- Implementation of AdderStb
package body AdderStb is separate;
begin
for Index in New_Array'FIRST..New_Array'LAST loop
New_Array(Index) := FLOAT(Index);
end loop;
Put_Line("Call Add_Em_Up now.");
Add_Em_Up(New_Array, Sum_Of_Values);
Put("Back from Add_Em_Up, total is");
Put(Sum_Of_Values, 5, 2, 0);
New_Line;
end Adder3;
-- Result of execution
-- Call Add_Em_Up now
-- Back from Add_Em_Up, total is 27.00
-- Chapter 15 - Program 5
separate(Adder3)
-- Implementation of AdderStb
package body AdderStb is
procedure Add_Em_Up(In_Dat : in MY_ARRAY;
Sum : out FLOAT) is
Total : FLOAT;
begin
Total := 0.0;
for Index in In_Dat'FIRST..In_Dat'LAST loop
Total := Total + In_Dat(Index);
end loop;
Sum := Total;
end Add_Em_Up;
end AdderStb;
-- Chapter 15 - Program 6
-- Interface of AdderPkg
package AdderPkg is
Total : FLOAT; -- Global variable
type MY_ARRAY is array(INTEGER range <>) of FLOAT;
procedure Add_Em_Up(In_Dat : in MY_ARRAY;
Sum : out FLOAT);
end AdderPkg;
-- Implementation of AdderPkg
package body AdderPkg is
procedure Add_Em_Up(In_Dat : in MY_ARRAY;
Sum : out FLOAT) is
begin
for Index in In_Dat'FIRST..In_Dat'LAST loop
Total := Total + In_Dat(Index);
end loop;
Sum := Total;
end Add_Em_Up;
begin -- Initialization section
Total := 0.0;
end AdderPkg;
with Ada.Text_IO;
use Ada.Text_IO;
with AdderPkg;
use AdderPkg;
procedure Adder4 is
package Flt_IO is new Ada.Text_IO.Float_IO(FLOAT);
use Flt_IO;
FIRST : constant := 2;
LAST : constant := 7;
Sum_Of_Values : FLOAT;
New_Array : MY_ARRAY(FIRST..LAST);
procedure Summer(In_Dat : MY_ARRAY;
Sum : out FLOAT) renames AdderPkg.Add_Em_Up;
begin
for Index in New_Array'FIRST..New_Array'LAST loop
New_Array(Index) := FLOAT(Index);
end loop;
Put_Line("Call Add_Em_Up now.");
Add_Em_Up(New_Array, Sum_Of_Values);
Put("Back from Add_Em_Up, total is");
Put(Sum_Of_Values, 5, 2, 0);
New_Line;
-- The next three statements are identical
Add_Em_Up(New_Array,Sum_Of_Values);
AdderPkg.Add_Em_Up(New_Array, Sum_Of_Values);
Summer(New_Array, Sum_Of_Values);
end Adder4;
-- Result of execution
-- Call Add_Em_Up now
-- Back from Add_Em_Up, total is 27.00
-- Chapter 15 - Programming example 1
with Ada.Text_IO, Ada.Float_Text_IO;
use Ada.Text_IO, Ada.Float_Text_IO;
procedure CH15_1A is
FIRST : constant := 2;
LAST : constant := 7;
Sum_Of_Values : FLOAT;
-- Interface of Package1
package AdderPkg is
type MY_ARRAY is array(INTEGER range <>) of FLOAT;
procedure Add_Em_Up(In_Dat : in MY_ARRAY;
Sum : out FLOAT);
end AdderPkg;
use CH15_1A.AdderPkg;
New_Array : MY_ARRAY(FIRST..LAST);
-- Implementation of Package1
package body AdderPkg is separate;
begin
for Index in New_Array'FIRST..New_Array'LAST loop
New_Array(Index) := FLOAT(Index);
end loop;
Put_Line("Call Add_Em_Up now.");
Add_Em_Up(New_Array, Sum_Of_Values);
Put("Back from Add_Em_Up, total is");
Put(Sum_Of_Values, 5, 2, 0);
New_Line;
end CH15_1A;
-- Result of execution
-- Call Add_Em_Up now
-- Back from Add_Em_Up, total is 27.00
-- Chapter 15 - Programmming example 1
separate (CH15_1A)
-- Implementation of Package1
package body AdderPkg is
procedure Add_Em_Up(In_Dat : in MY_ARRAY;
Sum : out FLOAT) is
Total : FLOAT;
begin
Total := 0.0;
for Index in In_Dat'FIRST..In_Dat'LAST loop
Total := Total + In_Dat(Index);
end loop;
Sum := Total;
end Add_Em_Up;
end AdderPkg;
-- Chapter 15 - Programming example 2
with Ada.Text_IO;
procedure CH15_2 is
type MY_FIXED is delta 0.01 range 20.0..42.0;
type DAY is (MON, TUE, WED, THU, FRI, SAT, SUN);
type MY_INTEGER is range -13..323;
X_Value : FLOAT := 3.14;
Index : INTEGER := 27;
Count : MY_INTEGER;
What : BOOLEAN := TRUE;
Who : BOOLEAN := FALSE;
Size : MY_FIXED := 24.33;
Today : DAY := TUE;
package Int_IO is new Ada.Text_IO.Integer_IO(INTEGER);
package Flt_IO is new Ada.Text_IO.Float_IO(FLOAT);
package Enum_IO is new Ada.Text_IO.Enumeration_IO(BOOLEAN);
package Fix_IO is new Ada.Text_IO.Fixed_IO(MY_FIXED);
package Day_IO is new Ada.Text_IO.Enumeration_IO(DAY);
package New_Int_IO is new Ada.Text_IO.Integer_IO(MY_INTEGER);
begin
-- INTEGER outputs
Ada.Text_IO.Put("Index is --->"); Int_IO.Put(Index);
Ada.Text_IO.Put("<---"); Ada.Text_IO.New_Line;
Ada.Text_IO.Put("Index is --->"); Int_IO.Put(Index,3);
Ada.Text_IO.Put("<---"); Ada.Text_IO.New_Line;
Ada.Text_IO.Put("Index is --->"); Int_IO.Put(Index,8);
Ada.Text_IO.Put("<---"); Ada.Text_IO.New_Line(2);
-- FLOAT outputs
Ada.Text_IO.Put("Put(X_Value) -------->"); Flt_IO.Put(X_Value);
Ada.Text_IO.New_Line;
Ada.Text_IO.Put("Put(X_Value,5) ------>"); Flt_IO.Put(X_Value,5);
Ada.Text_IO.New_Line;
Ada.Text_IO.Put("Put(X_Value,5,5) ---->"); Flt_IO.Put(X_Value,5,5);
Ada.Text_IO.New_Line;
Ada.Text_IO.Put("Put(X_Value,5,5,0) -->"); Flt_IO.Put(X_Value,5,5,0);
Ada.Text_IO.New_Line(2);
-- MY_FIXED outputs
Ada.Text_IO.Put("Put(Size) -------->"); Fix_IO.Put(Size);
Ada.Text_IO.New_Line;
Ada.Text_IO.Put("Put(Size,5) ------>"); Fix_IO.Put(Size,5);
Ada.Text_IO.New_Line;
Ada.Text_IO.Put("Put(Size,5,5) ---->"); Fix_IO.Put(Size,5,5);
Ada.Text_IO.New_Line;
Ada.Text_IO.Put("Put(Size,5,5,0) -->"); Fix_IO.Put(Size,5,5,0);
Ada.Text_IO.New_Line(2);
-- BOOLEAN outputs
Ada.Text_IO.Put("What is ---->"); Enum_IO.Put(What);
Ada.Text_IO.Put("<---"); Ada.Text_IO.New_Line;
Ada.Text_IO.Put("Who is ----->"); Enum_IO.Put(Who);
Ada.Text_IO.Put("<---"); Ada.Text_IO.New_Line;
Ada.Text_IO.Put("What is ---->"); Enum_IO.Put(What,7);
Ada.Text_IO.Put("<---"); Ada.Text_IO.New_Line;
Ada.Text_IO.Put("Who is ----->"); Enum_IO.Put(Who,8);
Ada.Text_IO.Put("<---"); Ada.Text_IO.New_Line;
Ada.Text_IO.Put("TRUE is ---->"); Enum_IO.Put(TRUE);
Ada.Text_IO.Put("<---"); Ada.Text_IO.New_Line;
Ada.Text_IO.Put("FALSE is --->"); Enum_IO.Put(FALSE);
Ada.Text_IO.Put("<---"); Ada.Text_IO.New_Line(2);
-- Enumeration outputs
Ada.Text_IO.Put("Today is --->"); Day_IO.Put(Today);
Ada.Text_IO.Put("<---"); Ada.Text_IO.New_Line;
Ada.Text_IO.Put("Today is --->"); Day_IO.Put(Today,6);
Ada.Text_IO.Put("<---"); Ada.Text_IO.New_Line;
Ada.Text_IO.Put("Today is --->"); Day_IO.Put(Today,7);
Ada.Text_IO.Put("<---"); Ada.Text_IO.New_Line;
Ada.Text_IO.Put("WED is ----->"); Day_IO.Put(WED);
Ada.Text_IO.Put("<---"); Ada.Text_IO.New_Line;
Ada.Text_IO.Put("WED is ----->"); Day_IO.Put(WED,5);
Ada.Text_IO.Put("<---"); Ada.Text_IO.New_Line(2);
end CH15_2;
-- Result of execution
-- Index is ---> 27<---
-- Index is ---> 27<---
-- Index is ---> 27<---
--
-- Put(X_Value) --------> 3.14000000000000E+00
-- Put(X_Value,5) ------> 3.14000000000000E+00
-- Put(X_Value,5,5) ----> 3.14000E+00
-- Put(X_Value,5,5,0) --> 3.14000
--
-- Put(Size) --------> 24.33
-- Put(Size,5) ------> 24.33
-- Put(Size,5,5) ----> 24.32813
-- Put(Size,5,5,0) --> 24.32813
--
-- What is ---->TRUE<---
-- Who is ----->FALSE<---
-- What is ---->TRUE <---
-- Who is ----->FALSE <---
-- TRUE is ---->TRUE<---
-- FALSE is --->FALSE<---
--
-- Today is --->TUE<---
-- Today is --->TUE <---
-- Today is --->TUE <---
-- WED is ----->WED<---
-- WED is ----->WED <---
-- Chapter 15 - Programming example 3
-- Interface of NewAdderPkg
package NewAdderPkg is
type MY_ARRAY is array(INTEGER range <>) of FLOAT;
procedure Add_Em_Up(In_Dat : in MY_ARRAY;
Sum : out FLOAT);
end NewAdderPkg;
-- Implementation of NewAdderPkg
package body NewAdderPkg is
procedure Add_Em_Up(In_Dat : in MY_ARRAY;
Sum : out FLOAT) is
Total : FLOAT;
begin
Total := 0.0;
for Index in In_Dat'FIRST..In_Dat'LAST loop
Total := Total + In_Dat(Index);
end loop;
Sum := Total;
end Add_Em_Up;
end NewAdderPkg;
-- Result of execution
-- (This is not a stand alone package, so it has no output.)
-- Chapter 15 - Programming example 3
with Ada.Text_IO, Ada.Float_Text_IO;
use Ada.Text_IO, Ada.Float_Text_IO;
with NewAdderPkg;
use NewAdderPkg;
procedure CH15_3B is
FIRST : constant := 2;
LAST : constant := 7;
Sum_Of_Values : FLOAT;
New_Array : MY_ARRAY(FIRST..LAST);
procedure Summer(In_Dat : MY_ARRAY;
Sum : out FLOAT) renames NewAdderPkg.Add_Em_Up;
begin
for Index in New_Array'FIRST..New_Array'LAST loop
New_Array(Index) := FLOAT(Index);
end loop;
Put_Line("Call Add_Em_Up now.");
Add_Em_Up(New_Array, Sum_Of_Values);
Put("Back from Add_Em_Up, total is");
Put(Sum_Of_Values, 5, 2, 0);
New_Line;
-- The next three statements are identical
Add_Em_Up(New_Array, Sum_Of_Values);
NewAdderPkg.Add_Em_Up(New_Array, Sum_Of_Values);
Summer(New_Array, Sum_Of_Values);
end CH15_3B;
-- Result of execution
-- Call Add_Em_Up now
-- Back from Add_Em_Up, total is 27.00
-- Chapter 16 - Program 1
package CharStak is
procedure Push(In_Char : in CHARACTER); -- In_Char is added to the
-- stack if there is room.
procedure Pop(Out_Char : out CHARACTER); -- Out_Char is removed from
-- stack and returned if a
-- character is on stack.
-- else a blank is returned
function Is_Empty return BOOLEAN; -- TRUE if stack is empty
function Is_Full return BOOLEAN; -- TRUE if stack is full
function Current_Stack_Size return INTEGER;
procedure Clear_Stack; -- Reset the stack to empty
end CharStak;
package body CharStak is
Maximum_Size : constant := 25;
Stack_List : STRING(1..Maximum_Size); -- The stack itself, purposely
-- defined very small.
Top_Of_Stack : INTEGER := 0; -- This will always point to
-- the top entry on the stack.
procedure Push(In_Char : in CHARACTER) is
begin
if not Is_Full then
Top_Of_Stack := Top_Of_Stack + 1;
Stack_List(Top_Of_Stack) := In_Char;
end if;
end Push;
procedure Pop(Out_Char : out CHARACTER) is
begin
if Is_Empty then
Out_Char := ' ';
else
Out_Char := Stack_List(Top_Of_Stack);
Top_Of_Stack := Top_Of_Stack - 1;
end if;
end Pop;
function Is_Empty return BOOLEAN is
begin
return Top_Of_Stack = 0;
end Is_Empty;
function Is_Full return BOOLEAN is
begin
return Top_Of_Stack = Maximum_Size;
end Is_Full;
function Current_Stack_Size return INTEGER is
begin
return Top_Of_Stack;
end Current_Stack_Size;
procedure Clear_Stack is
begin
Top_Of_Stack := 0;
end Clear_Stack;
end CharStak;
-- Chapter 16 - Program 2
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
with CharStak;
use CharStak;
procedure TryStak is
Example : constant STRING := "This is the first test.";
Another : constant STRING :=
"This is another test and this should not fit.";
procedure Fill_The_Stack(Input_Line : STRING) is
begin
Clear_Stack;
for Index in 1..Input_Line'LAST loop
if Is_Full then
Put_Line("The stack is full, no more added.");
exit;
else
Push(Input_Line(Index));
end if;
end loop;
end Fill_The_Stack;
procedure Empty_The_Stack is
Char : CHARACTER;
begin
loop
if Is_Empty then
New_Line;
Put_Line("The stack is empty.");
exit;
else
Pop(Char);
Put(Char);
end if;
end loop;
end Empty_The_Stack;
begin
Put_Line(Example);
Fill_The_Stack(Example);
Empty_The_Stack;
New_Line;
Put_Line(Another);
Fill_The_Stack(Another);
Empty_The_Stack;
end TryStak;
-- Result of execution
-- This is the first test.
-- .tset tsrif eht si sihT
-- The stack is empty.
--
-- This is another test and should not fit.
-- The stack is full, no more added.
-- dna tset rehtona si sihT
-- The stack is empty.
-- Chapter 16 - Program 3
-- This is a dynamic string package which can be used as an aid
-- in writing string intensive programs. Ada only has a static
-- string capability, so this package was written as an example of
-- how the Ada programming language can be expanded. The basis
-- for this package is the dynamic string available with the
-- Borland International implementation of Pascal, TURBO Pascal.
-- A dynamic string is defined as an array of characters of maximum
-- length of 255. The dynamic length of the dynamic string is
-- stored in the string location with index = 0, so the dynamic
-- string must be defined with a lower limit of 0 and an upper
-- limit of whatever the desired maximum length of the string is
-- to be. The subtype STRING_SIZE below limits the string length
-- when it is defined.
-- Put Outputs a dynamic string to the monitor
-- ConCat Concatenates two dynamic strings and puts the result
-- into a third dynamic string
-- Copy Copies a dynamic string to another dynamic string
-- Copy Copies a static string to a dynamic string
-- Delete Deletes a group of characters from a dynamic string
-- Insert Inserts a group of characters into a dynamic string
-- Length Returns the dynamic length of a dynamic string
-- Size_Of Returns the static length of a dynamic string
-- Pos Returns the first location of a dynamic string within
-- another dynamic string
with Ada.Text_IO; use Ada.Text_IO;
package DynStrng is
subtype STRING_SIZE is INTEGER range 0..255;
type DYNAMIC_STRING is array(STRING_SIZE range <>) of CHARACTER;
-- Put : Display a dynamic string on the monitor.
procedure Put(Input_String : in DYNAMIC_STRING);
-- ConCat : Concatenation - The First_String is copied into the
-- Result_String, then the Second_String is copied
-- into the Result_String following the First_String.
-- If all characters fit, Result is set to TRUE.
-- If Result_String will not hold all characters,
-- only as many as will fit are copied and Result
-- is set to FALSE.
-- Result = TRUE, complete copy done.
-- Result = FALSE, some or all not copied
procedure ConCat(First_String : in DYNAMIC_STRING;
Second_String : in DYNAMIC_STRING;
Result_String : in out DYNAMIC_STRING;
Result : out BOOLEAN);
-- Copy : The String contained in Input_String is copied into
-- the string Output_String. This procedure is
-- overloaded to include copying from either dynamic
-- strings or static strings.
-- Result = TRUE, complete copy done
-- Result = FALSE, some or all not copied
procedure Copy(Input_String : in DYNAMIC_STRING;
Output_String : in out DYNAMIC_STRING;
Result : out BOOLEAN);
procedure Copy(Input_String : in STRING;
Output_String : out DYNAMIC_STRING;
Result : out BOOLEAN);
-- Delete : Beginning at First_Position, as many characters as are
-- indicated by Number_Of_Characters are deleted from
-- String_To_Modify. If there are not that many, only
-- as many as are left are deleted.
-- Result = TRUE, deletion was complete
-- Result = FALSE, only a partial deletion was done
procedure Delete(String_To_Modify : in out DYNAMIC_STRING;
First_Position : in STRING_SIZE;
Number_Of_Characters : in STRING_SIZE;
Result : out BOOLEAN);
-- Insert : The string String_To_Insert is inserted into the string
-- String_To_Modify begining at location Place_To_Insert
-- if there is enough room.
-- Result = TRUE, insert completed in full
-- Result = FALSE, not enough room for full insert
procedure Insert(String_To_Modify : in out DYNAMIC_STRING;
String_To_Insert : in DYNAMIC_STRING;
Place_To_Insert : in STRING_SIZE;
Result : out BOOLEAN);
-- Length : Returns the dynamic length of the string defined by
-- String_To_Measure.
function Length(String_To_Measure : in DYNAMIC_STRING)
return STRING_SIZE;
-- Size_Of : Returns the static length of the string, the actual
-- upper limit of the string definition.
function Size_Of(String_To_Measure : in DYNAMIC_STRING)
return STRING_SIZE;
-- Pos : Position of substring - The string String_To_Search is
-- searched for the string Required_String beginning
-- at Place_To_Start.
-- Result = TRUE, a search was possible
-- Result = FALSE, no search could be made
-- Location_Found = 0, no string found
-- Location_Found = N, start of matching string
procedure Pos(String_To_Search : in DYNAMIC_STRING;
Required_String : in DYNAMIC_STRING;
Place_To_Start : in STRING_SIZE;
Location_Found : out STRING_SIZE;
Result : out BOOLEAN);
end DynStrng;
package body DynStrng is
-- The display procedure overloads the existing
-- Put procedures to output a dynamic string. Note
-- that the existing Put is used in this new Put.
procedure Put(Input_String : in DYNAMIC_STRING) is
begin
for Index in 1..CHARACTER'POS(Input_String(0)) loop
Put(Input_String(Index));
end loop;
end Put;
procedure ConCat(First_String : in DYNAMIC_STRING;
Second_String : in DYNAMIC_STRING;
Result_String : in out DYNAMIC_STRING;
Result : out BOOLEAN) is
Intermediate_Result : BOOLEAN;
Character_Count : STRING_SIZE;
Room_Left : STRING_SIZE;
begin
-- Copy the first into the result string
Copy(First_String,Result_String,Intermediate_Result);
if Intermediate_Result then
Character_Count := CHARACTER'POS(Second_String(0));
Room_Left := Result_String'LAST
- CHARACTER'POS(Result_String(0));
Result := TRUE;
if Character_Count > Room_Left then
Character_Count := Room_Left;
Result := FALSE;
end if;
for Index in 1..Character_Count loop
Result_String(Index + CHARACTER'POS(Result_String(0))) :=
Second_String(Index);
end loop;
Result_String(0) :=
CHARACTER'VAL(CHARACTER'POS(Result_String(0))
+ Character_Count);
else
Result := FALSE;
end if;
end ConCat;
-- This procedure overloads the name Copy to
-- copy from one dynamic string to another.
procedure Copy(Input_String : in DYNAMIC_STRING;
Output_String : in out DYNAMIC_STRING;
Result : out BOOLEAN) is
Character_Count : STRING_SIZE;
begin
-- First pick the smallest string size
Character_Count := CHARACTER'POS(Input_String(0));
if Character_Count > Output_String'LAST then
Character_Count := Output_String'LAST;
Result := FALSE; -- The entire string didn't fit
else
Result := TRUE; -- The entire string fit
end if;
for Index in 0..Character_Count loop
Output_String(Index) := Input_String(Index);
end loop;
Output_String(0) := CHARACTER'VAL(Character_Count);
end Copy;
-- This routine overloads the copy procedure name
-- to copy a static string into a dynamic string.
procedure Copy(Input_String : in STRING;
Output_String : out DYNAMIC_STRING;
Result : out BOOLEAN) is
Character_Count : STRING_SIZE;
begin
-- First pick the smallest string size
Character_Count := Input_String'LAST;
if Character_Count > Output_String'LAST then
Character_Count := Output_String'LAST;
Result := FALSE; -- The entire string didn't fit
else
Result := TRUE; -- The entire string fit
end if;
for Index in 1..Character_Count loop
Output_String(Index) := Input_String(Index);
end loop;
Output_String(0) := CHARACTER'VAL(Character_Count);
end Copy;
procedure Delete(String_To_Modify : in out DYNAMIC_STRING;
First_Position : in STRING_SIZE;
Number_Of_Characters : in STRING_SIZE;
Result : out BOOLEAN) is
Number_To_Delete : STRING_SIZE;
Number_To_Move : STRING_SIZE;
Last_Dynamic_Character : STRING_SIZE :=
CHARACTER'POS(String_To_Modify(0));
begin
-- can we delete any at all?
if First_Position > Last_Dynamic_Character then
Result := FALSE;
return;
end if;
-- Decide how many to delete
if (First_Position + Number_Of_Characters)
> Last_Dynamic_Character then
Number_To_Delete := Last_Dynamic_Character
- First_Position + 1;
Result := FALSE;
else
Number_To_Delete := Number_Of_Characters;
Result := TRUE;
end if;
-- find out how many to move back
if (Last_Dynamic_Character - (First_Position + Number_To_Delete))
>= 0 then
Number_To_Move := 1 + Last_Dynamic_Character
- (First_Position + Number_To_Delete);
else
Number_To_Move := 0;
end if;
-- now delete the characters by moving them back
for Index in First_Position..
(First_Position + Number_To_Move - 1) loop
String_To_Modify(Index) := String_To_Modify(Index
+ Number_To_Delete);
end loop;
String_To_Modify(0) :=
CHARACTER'VAL(CHARACTER'POS(String_To_Modify(0))
- Number_To_Delete);
end Delete;
procedure Insert(String_To_Modify : in out DYNAMIC_STRING;
String_To_Insert : in DYNAMIC_STRING;
Place_To_Insert : in STRING_SIZE;
Result : out BOOLEAN) is
Number_To_Add : STRING_SIZE;
Number_To_Move : STRING_SIZE;
Room_Left : STRING_SIZE;
begin
-- Can we add any at all?
if (Place_To_Insert > String_To_Modify'LAST) or
(Place_To_Insert > CHARACTER'POS(String_To_Modify(0)) + 1) then
Result := FALSE;
return;
end if;
Result := TRUE;
-- How many can we add?
Number_To_Add := String_To_Modify'LAST - Place_To_Insert + 1;
if Number_To_Add > CHARACTER'POS(String_To_Insert(0)) then
Number_To_Add := CHARACTER'POS(String_To_Insert(0));
end if;
-- Find how many to move forward
Number_To_Move := CHARACTER'POS(String_To_Modify(0))
- Place_To_Insert + 1;
Room_Left := String_To_Modify'LAST - Place_To_Insert + 1;
if Room_Left < Number_To_Move then
Number_To_Move := Room_Left;
end if;
-- Move them forward
for Index in reverse Place_To_Insert..Place_To_Insert
+ Number_To_Move - 1 loop
String_To_Modify(Index + Number_To_Add) :=
String_To_Modify(Index);
end loop;
for Index in 1..Number_To_Add loop
String_To_Modify(Index + Place_To_Insert - 1) :=
String_To_Insert(Index);
end loop;
-- Increase the length of the string
String_To_Modify(0) := CHARACTER'VAL(
CHARACTER'POS(String_To_Modify(0)) + Number_To_Add);
if CHARACTER'POS(String_To_Modify(0)) > String_To_Modify'LAST
then
String_To_Modify(0) := CHARACTER'VAL(String_To_Modify'LAST);
end if;
end Insert;
-- This returns the dynamic length of a string
function Length(String_To_Measure : in DYNAMIC_STRING)
return STRING_SIZE is
begin
return CHARACTER'POS(String_To_Measure(0));
end Length;
-- This returns the static length of a string
function Size_Of(String_To_Measure : in DYNAMIC_STRING)
return STRING_SIZE is
begin
return String_To_Measure'LAST;
end Size_Of;
procedure Pos(String_To_Search : in DYNAMIC_STRING;
Required_String : in DYNAMIC_STRING;
Place_To_Start : in STRING_SIZE;
Location_Found : out STRING_SIZE;
Result : out BOOLEAN) is
End_Search : STRING_SIZE;
Characters_All_Compare : BOOLEAN;
begin
Location_Found := 0;
-- can we search the string at all?
if (Place_To_Start < CHARACTER'POS(String_To_Search(0))) and
(Place_To_Start < String_To_Search'LAST) then
Result := TRUE;
else
Result := FALSE;
return;
end if;
-- search the loop for the string now
End_Search := CHARACTER'POS(String_To_Search(0)) -
CHARACTER'POS(Required_String(0)) + 1;
for Index in Place_To_Start..End_Search loop -- search loop
Characters_All_Compare := TRUE;
for Count in 1..CHARACTER'POS(Required_String(0)) loop
if Required_String(Count) /=
String_To_Search(Count + Index - 1) then
Characters_All_Compare := FALSE;
exit; -- exit loop, a character did not match
end if;
end loop;
if Characters_All_Compare then
Location_Found := Index;
return; -- string match found, return location
end if;
end loop; -- end search loop
end Pos;
end DynStrng;
-- Chapter 16 - Program 4
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_text_IO; use Ada.Integer_Text_IO;
with DynStrng; use DynStrng;
procedure TryStrng is
Try_This : STRING(1..13);
Name : DYNAMIC_STRING(0..15);
Stuff : DYNAMIC_STRING(0..35);
Result : BOOLEAN;
Neat : constant STRING := "XYZ";
Good3 : STRING(1..3);
Good4 : STRING(1..4);
Column : INTEGER;
begin
Name(0) := CHARACTER'VAL(3);
Stuff(0) := CHARACTER'VAL(7);
Put(Size_Of(Name));
Put(Size_Of(Stuff));
Put(Length(Name));
Put(Length(Stuff));
New_Line;
Try_This := "ABCDEFGHIJKL$";
Copy(Try_This,Stuff,Result);
Put(Size_Of(Stuff));
Put(Length(Stuff));
Put(Stuff); Put(Stuff);
New_Line(2);
Copy(Stuff,Name,Result);
Put(Name); Put(Name); Put(Name); New_Line;
Concat(Name,Name,Stuff,Result);
Put(Stuff); New_Line;
Delete(Stuff,5,3,Result);
Put(Stuff); New_Line;
Delete(Stuff,6,3,Result);
Put(Stuff); New_Line;
Delete(Stuff,6,3,Result);
Put(Stuff); New_Line;
Delete(Stuff,6,3,Result);
Put(Stuff); New_Line;
Delete(Stuff,6,3,Result);
Put(Stuff); New_Line;
Delete(Stuff,6,3,Result);
Put(Stuff); New_Line;
Delete(Stuff,6,3,Result);
Put(Stuff); New_Line;
Delete(Stuff,6,3,Result);
Put(Stuff); New_Line(2);
Try_This := "1234567890123";
Copy(Try_This,Stuff,Result);
Copy(Neat,Name,Result);
Put(Stuff); Put(Name); New_Line;
Insert(Stuff,Name,5,Result);
Put(Stuff); New_Line;
Insert(Stuff,Name,50,Result);
Put(Stuff); New_Line;
Insert(Stuff,Name,2,Result);
Put(Stuff); New_Line;
Insert(Stuff,Name,24,Result);
Put(Stuff); New_Line;
Insert(Stuff,Name,5,Result);
Put(Stuff); New_Line;
Insert(Stuff,Name,5,Result);
Put(Stuff); New_Line;
Insert(Stuff,Name,5,Result);
Put(Stuff); New_Line;
Insert(Stuff,Name,5,Result);
Put(Stuff); New_Line(2);
Good3 := "123";
Try_This := "1234567890123";
Copy(Try_This,Stuff,Result);
Copy(Good3,Name,Result);
Pos(Stuff,Name,1,Column,Result);
Ada.Text_IO.Put("Found in column number"); Put(Column); New_Line;
Pos(Stuff,Name,2,Column,Result);
Ada.Text_IO.Put("Found in column number"); Put(Column); New_Line;
Pos(Stuff,Name,7,Column,Result);
Ada.Text_IO.Put("Found in column number"); Put(Column); New_Line;
Pos(Stuff,Name,12,Column,Result);
Ada.Text_IO.Put("Found in column number"); Put(Column); New_Line;
Pos(Stuff,Name,18,Column,Result);
Ada.Text_IO.Put("Found in column number"); Put(Column); New_Line;
Pos(Stuff,Name,50,Column,Result);
Ada.Text_IO.Put("Found in column number"); Put(Column); New_Line;
end TryStrng;
-- Chapter 16 - Program 5
-- This program will calculate the number of days old you are.
-- It is a rather dumb program, but illustrates some interesting
-- programming techniques. It checks all input to see that they
-- are in the correct range before continuing. Since the number
-- of days can easily exceed the limits of type INTEGER, and we
-- cannot count on LONG_INTEGER being available, a fixed point
-- variable is used for the total number of days since Jan 1, 1880.
-- This program also passes a record to a procedure, where it is
-- modified and returned.
with Ada.Text_IO, Ada.Integer_Text_IO;
use ADa.Text_IO, Ada.Integer_Text_IO;
procedure Age is
LOW_YEAR : constant := 1880;
MAX : constant := 365.0 * (2100 - LOW_YEAR);
type AGES is delta 1.0 range -MAX..MAX;
Present_Age : AGES;
package Fix_IO is new Ada.Text_IO.Fixed_IO(AGES);
use Fix_IO;
type DATE is record
Month : INTEGER range 1..12;
Day : INTEGER range 1..31;
Year : INTEGER range LOW_YEAR..2100;
Days : AGES;
end record;
Today : DATE;
Birth_Day : DATE;
procedure Get_Date(Date_To_Get : in out DATE) is
Temp : INTEGER;
begin
Put(" month --> ");
loop
Get(Temp);
if Temp in 1..12 then
Date_To_Get.Month := Temp;
exit; -- month OK
else
Put_Line(" Month must be in the range of 1 to 12");
Put(" ");
Put(" month --> ");
end if;
end loop;
Put(" ");
Put(" day ----> ");
loop
Get(Temp);
if Temp in 1..31 then
Date_To_Get.Day := Temp;
exit; -- day OK
else
Put_Line(" Day must be in the range of 1 to 31");
Put(" ");
Put(" day ----> ");
end if;
end loop;
Put(" ");
Put(" year ---> ");
loop
Get(Temp);
if Temp in LOW_YEAR..2100 then
Date_To_Get.Year := Temp;
exit; -- year OK
else
Put_Line(" Year must be in the range of 1880 to 2100");
Put(" ");
Put(" year ---> ");
end if;
end loop;
Date_To_Get.Days := 365 * AGES(Date_To_Get.Year - LOW_YEAR)
+ AGES(31 * Date_To_Get.Month + Date_To_Get.Day);
end Get_Date;
begin
Put("Enter Today's date; ");
Get_Date(Today);
New_Line;
Put("Enter your birthday;");
Get_Date(Birth_Day);
New_Line(2);
Present_Age := Today.Days - Birth_Day.Days;
if Present_Age < 0.0 then
Put("You will be born in ");
Present_Age := abs(Present_Age);
Put(Present_Age, 6, 0, 0);
Put_Line(" days.");
elsif Present_Age = 0.0 then
Put_Line("Happy birthday, you were just born today.");
else
Put("You are now ");
Put(Present_Age, 6, 0, 0);
Put_Line(" days old.");
end if;
end Age;
-- Chapter 16 - Programming exercise 1
package CharStak is
function Room_Left return INTEGER; -- Room left on stack
procedure Push(In_Char : in CHARACTER); -- In_Char is added to the
-- stack if there is room.
procedure Pop(Out_Char : out CHARACTER); -- Out_Char is removed from
-- stack and returned if a
-- character is on stack.
-- else a blank is returned
function Is_Empty return BOOLEAN; -- TRUE if stack is empty
function Is_Full return BOOLEAN; -- TRUE if stack is full
function Current_Stack_Size return INTEGER;
procedure Clear_Stack; -- Reset the stack to empty
end CharStak;
package body CharStak is
Maximum_Size : constant := 25;
Stack_List : STRING(1..Maximum_Size); -- The stack itself, purposely
-- defined very small.
Top_Of_Stack : INTEGER := 0; -- This will always point to
-- the top entry on the stack.
function Room_Left return INTEGER is
begin
return Maximum_Size - Top_Of_Stack;
end Room_Left;
procedure Push(In_Char : in CHARACTER) is
begin
if not Is_Full then
Top_Of_Stack := Top_Of_Stack + 1;
Stack_List(Top_Of_Stack) := In_Char;
end if;
end Push;
procedure Pop(Out_Char : out CHARACTER) is
begin
if Is_Empty then
Out_Char := ' ';
else
Out_Char := Stack_List(Top_Of_Stack);
Top_Of_Stack := Top_Of_Stack - 1;
end if;
end Pop;
function Is_Empty return BOOLEAN is
begin
return Top_Of_Stack = 0;
end Is_Empty;
function Is_Full return BOOLEAN is
begin
return Top_Of_Stack = Maximum_Size;
end Is_Full;
function Current_Stack_Size return INTEGER is
begin
return Top_Of_Stack;
end Current_Stack_Size;
procedure Clear_Stack is
begin
Top_Of_Stack := 0;
end Clear_Stack;
end CharStak;
-- Chapter 16 - Programming example 2
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
with CharStak;
use CharStak;
procedure CH16_2 is
Example : constant STRING := "This is the first test.";
Another : constant STRING :=
"This is another test and this should not fit.";
Number_Of_Blank_Spaces : INTEGER;
procedure Fill_The_Stack(Input_Line : STRING) is
begin
Clear_Stack;
for Index in 1..Input_Line'LAST loop
if Is_Full then
Put_Line("The stack is full, no more added.");
exit;
else
Push(Input_Line(Index));
end if;
end loop;
end Fill_The_Stack;
procedure Empty_The_Stack is
Char : CHARACTER;
begin
loop
if Is_Empty then
New_Line;
Put_Line("The stack is empty.");
exit;
else
Pop(Char);
Put(Char);
end if;
end loop;
end Empty_The_Stack;
begin
Put_Line(Example);
Fill_The_Stack(Example);
Number_Of_Blank_Spaces := Room_Left;
Put("Room left on stack =");
Put(Number_Of_Blank_Spaces); New_Line;
Empty_The_Stack;
New_Line;
Put_Line(Another);
Fill_The_Stack(Another);
Put("Room left on stack ="); Put(Room_Left); New_Line;
Empty_The_Stack;
end CH16_2;
-- Result of execution
-- This is the first test.
-- Room left on stack = 2
-- .tset tsrif eht si sihT
-- The stack is empty.
--
-- This is another test and should not fit.
-- The stack is full, no more added.
-- Room left on stack = 0
-- dna tset rehtona si sihT
-- The stack is empty.
-- Chapter 17 - Program 1
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Except1 is
procedure Divide_Loop is
Divide_Result : INTEGER;
begin
for Index in 1..8 loop
Put("Index is");
Put(Index, 3);
Put(" and the answer is");
Divide_Result := 25 / (4 - Index);
Put(Divide_Result, 3);
New_Line;
end loop;
exception
when Constraint_Error => Put_Line(" Divide by zero error.");
end Divide_Loop;
begin
Put_Line("Begin program here.");
Divide_Loop;
Put_Line("End of program.");
end Except1;
-- Result of Execution
-- Begin program here.
-- Index is 1 and the answer is 8
-- Index is 2 and the answer is 12
-- Index is 3 and the answer is 25
-- Index is 4 and the answer is Divide by zero error.
-- End of program.
-- Chapter 17 - Program 2
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Except2 is
procedure Divide_Loop(Index : in INTEGER) is
Divide_Result : INTEGER;
begin
Put("Index is");
Put(Index, 3);
Put(" and the answer is");
Divide_Result := 25 / (4 - Index);
Put(Divide_Result, 4);
New_Line;
exception
when Constraint_Error => Put(" Divide by zero error");
Put_Line(" in loop 1.");
end Divide_Loop;
procedure New_Divide_Loop(Index : in INTEGER) is
My_Own_Exception : exception;
Divide_Result : INTEGER;
begin
Put("Index is");
Put(Index, 3);
Put(" and the answer is");
if Index = 4 then
raise My_Own_Exception;
end if;
Divide_Result := 25 / (4 - Index);
Put(Divide_Result, 4);
New_Line;
exception
when My_Own_Exception => Put(" Divide by zero error");
Put_Line(" in loop 3.");
when Constraint_Error => Put_Line("This shouldn't happen.");
Put_Line("But is included anyway.");
when others => Put_Line("Some other exception found.");
end New_Divide_Loop;
begin
Put_Line("Begin program here.");
for Count in 1..6 loop -- begin loop number 1
Divide_Loop(Count);
end loop;
Put_Line("End of first loop.");
for Count in 1..6 loop -- begin loop number 2
declare
Divide_Result : INTEGER;
begin
Put("Count is");
Put(Count, 3);
Put(" and the answer is");
Divide_Result := 25 / (4 - Count);
Put(Divide_Result, 4);
New_Line;
exception
when Constraint_Error => Put(" Divide by zero error");
Put_Line(" in loop 2.");
end;
end loop;
Put_Line("End of second loop.");
for Count in 1..6 loop -- begin loop number 3
New_Divide_Loop(Count);
end loop;
Put_Line("End of program.");
end Except2;
-- Result of Execution
-- Begin program here.
-- Index is 1 and the answer is 8
-- Index is 2 and the answer is 12
-- Index is 3 and the answer is 25
-- Index is 4 and the answer is Divide by zero error in loop 1.
-- Index is 5 and the answer is -25
-- Index is 6 and the answer is -12
-- End of first loop.
-- Count is 1 and the answer is 8
-- Count is 2 and the answer is 12
-- Count is 3 and the answer is 25
-- Count is 4 and the answer is Divide by zero error in loop 2.
-- Count is 5 and the answer is -25
-- Count is 6 and the answer is -12
-- End of second loop.
-- Index is 1 and the answer is 8
-- Index is 2 and the answer is 12
-- Index is 3 and the answer is 25
-- Index is 4 and the answer is Divide by zero error in loop 3.
-- Index is 5 and the answer is -25
-- Index is 6 and the answer is -12
-- End of program.
-- Chapter 17 - Program 3
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Except3 is
procedure Divide_By_Zero(Count : INTEGER) is
Divide_Result : INTEGER;
begin
Put("Count is");
Put(Count, 3);
Put(" and the answer is");
Divide_Result := 25 / (Count - 4);
Put(Divide_Result, 4);
New_Line;
exception
when Constraint_Error => Put_Line(" Divide by zero occurred");
end Divide_By_Zero;
procedure Raise_An_Error(Count : INTEGER) is
My_Own_Error : exception;
Another_Result : INTEGER;
begin
Put("Count is");
Put(Count, 3);
Another_Result := 35 / (Count - 6); -- untested divide by zero
if Count = 3 then
raise My_Own_Error;
end if;
Put_Line(" and is a legal value");
exception
when My_Own_Error => Put_Line(" My own error occurred");
end Raise_An_Error;
begin
Put_Line("Begin program here.");
for Count in 1..7 loop
Divide_By_Zero(Count);
Raise_An_Error(Count);
end loop;
Put_Line("End of program.");
exception
when Constraint_Error => Put(" Constraint error detected at");
Put_Line(" the main program level.");
Put_Line("Program terminated.");
end Except3;
-- Result of Execution
-- Begin program here.
-- Count is 1 and the answer is -8
-- Count is 1 and is a legal value
-- Count is 2 and the answer is -12
-- Count is 2 and is a legal value
-- Count is 3 and the answer is -25
-- Count is 3 My own error occurred
-- Count is 4 and the answer is Divide by zero occurred
-- Count is 4 and is a legal value
-- Count is 5 and the answer is 25
-- Count is 5 and is a legal value
-- Count is 6 and the answer is 12
-- Count is 6 Constraint error detected at the main program level.
-- Program terminated.
-- Chapter 17 - Program 4
with Ada.Text_IO;
use Ada.Text_IO;
procedure Except4 is
procedure Try_It is
VALUE : constant := 8;
subtype LIMIT_RANGE is INTEGER range 14..33;
Funny : LIMIT_RANGE := VALUE;
begin
Put_Line("We are in the Try_It procedure");
exception
when Constraint_Error =>
Put_Line("Constraint error occurred");
Put_Line("Detected in procedure Try_It");
end Try_It;
procedure Try_To_Fix_It is
begin
Put_Line("We are in the Try_To_Fix_It procedure.");
Try_It;
exception
when Constraint_Error =>
Put_Line("Constraint error occurred");
Put_Line("Detected in procedure Try_To_Fix_It");
end Try_To_Fix_It;
begin
Put_Line("Begin program here.");
for Count in 1..4 loop
Put_Line("Ready to call Try_To_Fix_It.");
Try_To_Fix_It;
New_Line;
end loop;
Put_Line("End of program.");
exception
when Constraint_Error =>
Put ("Range error detected at the");
Put_Line(" main program level.");
Put_Line("Program terminated.");
end Except4;
-- Result of execution
-- Begin program here.
-- Ready to call Try_To_Fix_It.
-- We are in the Try_To_Fix_It procedure.
-- Constraint error occurred
-- Detected in procedure Try_To-Fix_It
--
-- Ready to call Try_To_Fix_It.
-- We are in the Try_To_Fix_It procedure.
-- Constraint error occurred
-- Detected in procedure Try_To-Fix_It
--
-- Ready to call Try_To_Fix_It.
-- We are in the Try_To_Fix_It procedure.
-- Constraint error occurred
-- Detected in procedure Try_To-Fix_It
--
-- Ready to call Try_To_Fix_It.
-- We are in the Try_To_Fix_It procedure.
-- Constraint error occurred
-- Detected in procedure Try_To-Fix_It
--
-- End of program.
-- Chapter 17 - Program 5
package Stuff is
Funny_Add_Error : exception;
procedure Add_One(Number : in out INTEGER);
function Subtract_One(Number : INTEGER) return INTEGER;
end Stuff;
with Ada.Text_IO;
use Ada.Text_IO;
package body Stuff is
procedure Add_One(Number : in out INTEGER) is
begin
Number := Number + 1;
exception
when Funny_Add_Error => Put_Line("Funny add error raised");
when Constraint_Error => Put_Line("Constraint error raised");
end Add_One;
function Subtract_One(Number : INTEGER) return INTEGER is
Funny_Subtract_Error : exception;
begin
raise Funny_Subtract_Error;
return Number - 1;
exception
when Funny_Add_Error =>
Put_Line("Funny add error raised");
raise;
when Funny_Subtract_Error =>
Put_Line("Funny subtract error raised");
raise;
end Subtract_One;
begin
null;
exception
-- Numeric_Error is obsolete in Ada 95. It is another name for
-- the exception Constraint_Error.
-- when Numeric_Error =>
-- Put_Line("Numeric error during elaboration");
when Constraint_Error =>
Put_Line("Constraint_Error during elaboration");
when Funny_Add_Error =>
Put_Line("Funny Add error during elaboration");
-- when Funny_Subtract_Error =>
-- Put_Line("Funny subtract error during elaboration");
end Stuff;
with Ada.Text_IO, Stuff;
use Ada.Text_IO, Stuff;
procedure Except5 is
Index : INTEGER := 5;
Add_Error : exception renames Stuff.Funny_Add_Error;
begin
Add_One(Index);
Index := Subtract_One(Index);
exception
when Numeric_Error | Constraint_Error =>
Put_Line("Numeric error or constraint error raised.");
when Add_Error =>
Put_Line("Addition error detected");
when others =>
Put_Line("An unknown exception raised");
raise; -- Raise it again for the operating system
end Except5;
-- Result of execution
-- Funny subtract error raised
-- An unknown exception raised
-- Unhandled exception; funny_subtract_error
-- (Note, The last line above will be different for each compiler,
-- but will say something about an unhandled exception. It
-- will probably output about 10 lines of text.)
-- Chapter 17 - Program 6
with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Exceptions;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Occur is
Except_ID : Ada.Exceptions.EXCEPTION_OCCURRENCE;
procedure Divide_Loop is
Divide_Result : INTEGER;
begin
for Index in 1..8 loop
Put("Index is");
Put(Index, 3);
Put(" and the answer is");
Divide_Result := 25 / (4 - Index);
Put(Divide_Result, 3);
New_Line;
end loop;
exception
when Except_ID: Constraint_Error =>
Put_Line(" Divide by zero error.");
New_Line;
Put_Line(" Exception name:");
Put_Line(Ada.Exceptions.Exception_Name(Except_ID));
New_Line;
Put_Line(" Exception information:");
Put_Line(Ada.Exceptions.Exception_Information(Except_ID));
New_Line;
when Except_ID: Storage_Error =>
Put_Line(" Storage error detected.");
Put_Line(Ada.Exceptions.Exception_Information(Except_ID));
New_Line;
when Except_ID: others =>
Put_Line(" Unknown exception detected.");
Put_Line(Ada.Exceptions.Exception_Information(Except_ID));
New_Line;
end Divide_Loop;
begin
Put_Line("Begin program here.");
Divide_Loop;
Put_Line("End of program.");
end Occur;
-- Result of Execution
-- Begin program here.
-- Index is 1 and the answer is 8
-- Index is 2 and the answer is 12
-- Index is 3 and the answer is 25
-- Index is 4 and the answer is Divide by zero error.
--
-- Exception name:
-- Constraint_Error
--
-- Exception message:
-- Constraint_Error (divide by zero)
--
-- End of program.
-- Chapter 17 - Programming example 1
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure CH17_1 is
procedure Divide_Loop(Index : in INTEGER) is
Divide_Result : INTEGER;
begin
Put("Index is");
Put(Index, 3);
Put(" and the answer is");
Divide_Result := 25 / (4 - Index);
Put(Divide_Result);
New_Line;
exception
when Constraint_Error => Put(" Divide by zero error");
Put_Line(" in loop 1.");
end Divide_Loop;
procedure New_Divide_Loop(Index : in INTEGER) is
My_Own_Exception : exception;
Divide_Result : INTEGER;
begin
Put("Index is");
Put(Index, 3);
Put(" and the answer is");
if Index = 4 then
raise My_Own_Exception;
end if;
Divide_Result := 25 / (2 - Index);
Put(Divide_Result);
New_Line;
exception
when My_Own_Exception => Put(" Divide by zero error");
Put_Line(" in loop 3.");
when Constraint_Error => Put_Line("This shouldn't happen.");
Put_Line("But is included anyway.");
when others => Put_Line("Some other exception found.");
end New_Divide_Loop;
begin
Put_Line("Begin program here.");
for Count in 1..6 loop -- begin loop number 1
Divide_Loop(Count);
end loop;
Put_Line("End of first loop.");
for Count in 1..6 loop -- begin loop number 2
declare
Divide_Result : INTEGER;
begin
Put("Count is");
Put(Count, 3);
Put(" and the answer is");
Divide_Result := 25 / (4 - Count);
Put(Divide_Result);
New_Line;
exception
when Constraint_Error => Put(" Divide by zero error");
Put_Line(" in loop 2.");
end;
end loop;
Put_Line("End of second loop.");
for Count in 1..6 loop -- begin loop number 3
New_Divide_Loop(Count);
end loop;
Put_Line("End of program.");
end CH17_1;
-- Result of Execution
-- Begin program here.
-- Index is 1 and the answer is 8
-- Index is 2 and the answer is 12
-- Index is 3 and the answer is 25
-- Index is 4 and the answer is Divide by zero error in loop 1.
-- Index is 5 and the answer is -25
-- Index is 6 and the answer is -12
-- End of first loop.
-- Count is 1 and the answer is 8
-- Count is 2 and the answer is 12
-- Count is 3 and the answer is 25
-- Count is 4 and the answer is Divide by zero error in loop 2.
-- Count is 5 and the answer is -25
-- Count is 6 and the answer is -12
-- End of second loop.
-- Index is 1 and the answer is 25
-- Index is 2 and the answer isThis shouldn't happen.
-- But is included anyway.
-- Index is 3 and the answer is -25
-- Index is 4 and the answer is Divide by zero error in loop 3.
-- Index is 5 and the answer is -8
-- Index is 6 and the answer is -6
-- End of program.
-- Chapter 17 - Programming example 2
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure CH17_2 is
procedure Divide_Loop(Index : in INTEGER) is
Divide_Result : INTEGER;
begin
Put("Index is");
Put(Index, 3);
Put(" and the answer is");
Divide_Result := 25 / (4 - Index);
Put(Divide_Result);
New_Line;
exception
when Constraint_Error => Put(" Divide by zero error");
Put_Line(" in loop 1.");
end Divide_Loop;
procedure New_Divide_Loop(Index : in INTEGER) is
My_Own_Exception, Surprise : exception;
Divide_Result : INTEGER;
begin
Put("Index is");
Put(Index, 3);
Put(" and the answer is");
if Index = 4 then
raise Surprise;
end if;
Divide_Result := 25 / (4 - Index);
Put(Divide_Result);
New_Line;
exception
when My_Own_Exception => Put(" Divide by zero error");
Put_Line(" in loop 3.");
when Constraint_Error => Put_Line("This shouldn't happen.");
Put_Line("But is included anyway.");
when others => Put_Line(" Some other exception found.");
end New_Divide_Loop;
begin
Put_Line("Begin program here.");
for Count in 1..6 loop -- begin loop number 1
Divide_Loop(Count);
end loop;
Put_Line("End of first loop.");
for Count in 1..6 loop -- begin loop number 2
declare
Divide_Result : INTEGER;
begin
Put("Count is");
Put(Count, 3);
Put(" and the answer is");
Divide_Result := 25 / (4 - Count);
Put(Divide_Result);
New_Line;
exception
when Constraint_Error => Put(" Divide by zero error");
Put_Line(" in loop 2.");
end;
end loop;
Put_Line("End of second loop.");
for Count in 1..6 loop -- begin loop number 3
New_Divide_Loop(Count);
end loop;
Put_Line("End of program.");
end CH17_2;
-- Result of Execution
-- Begin program here.
-- Index is 1 and the answer is 8
-- Index is 2 and the answer is 12
-- Index is 3 and the answer is 25
-- Index is 4 and the answer is Divide by zero error in loop 1.
-- Index is 5 and the answer is -25
-- Index is 6 and the answer is -12
-- End of first loop.
-- Count is 1 and the answer is 8
-- Count is 2 and the answer is 12
-- Count is 3 and the answer is 25
-- Count is 4 and the answer is Divide by zero error in loop 2.
-- Count is 5 and the answer is -25
-- Count is 6 and the answer is -12
-- End of second loop.
-- Index is 1 and the answer is 8
-- Index is 2 and the answer is 12
-- Index is 3 and the answer is 25
-- Index is 4 and the answer is Some other exception found.
-- Index is 5 and the answer is -25
-- Index is 6 and the answer is -12
-- End of program.
-- Chapter 18 - Program 1
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Defaults is
Index : INTEGER;
Animal_Sum : INTEGER;
procedure Animals(Total : in out INTEGER;
Cows : in INTEGER := 0;
Pigs : in INTEGER := 0;
Dogs : in INTEGER := 0) is
begin
Total := Cows + Pigs + Dogs;
Put("Cows =");
Put(Cows, 3);
Put(" Pigs =");
Put(Pigs, 3);
Put(" Dogs =");
Put(Dogs, 3);
Put(" and they total");
Put(Total, 4);
New_Line;
end Animals;
begin
Index := 3;
Animals(Animal_Sum, 2, 3, 4);
Animals(Animal_Sum, 3, Index, 4);
Animals(Dogs => 4, Total => Animal_Sum);
Animals(Total => Animal_Sum, Pigs => 2 * Index + 1, Cows => 5);
Animals(Dogs => Index + 4, Total => Animal_Sum);
Animals(Animal_Sum, Dogs => 4, Pigs => Index, Cows => 2);
Animals(Animal_Sum);
end Defaults;
-- Result of Execution
-- Cows = 2 Pigs = 3 Dogs = 4 and they total 9
-- Cows = 3 Pigs = 3 Dogs = 4 and they total 10
-- Cows = 0 Pigs = 0 Dogs = 4 and they total 4
-- Cows = 5 Pigs = 7 Dogs = 0 and they total 12
-- Cows = 0 Pigs = 0 Dogs = 7 and they total 7
-- Cows = 2 Pigs = 3 Dogs = 4 and they total 9
-- Cows = 0 Pigs = 0 Dogs = 0 and they total 0
-- Chapter 18 - Program 2
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Default2 is
Index : INTEGER;
Animal_Sum : INTEGER;
function Cow_Constant return INTEGER is
begin
return 7;
end Cow_Constant;
function Pig_Constant return INTEGER is
Animals : INTEGER := Cow_Constant - 3;
begin
return 2 * Animals + 5;
end Pig_Constant;
procedure Animals(Total : in out INTEGER;
Cows : in INTEGER := 2 * Cow_Constant;
Pigs : in INTEGER := Cow_Constant +
Pig_Constant;
Dogs : in INTEGER := 0) is
begin
Total := Cows + Pigs + Dogs;
Put("Cows =");
Put(Cows, 3);
Put(" Pigs =");
Put(Pigs, 3);
Put(" Dogs =");
Put(Dogs, 3);
Put(" and they total");
Put(Total, 4);
New_Line;
end Animals;
begin
Index := 3;
Animals(Animal_Sum, 2, 3, 4);
Animals(Animal_Sum, 2, Index, 4);
Animals(Dogs => 4, Total => Animal_Sum);
Animals(Total => Animal_Sum, Pigs => 2 * Index + 1, Cows => 5);
Animals(Dogs => Index + 4, Total => Animal_Sum);
Animals(Animal_Sum, Dogs => 4, Pigs => Index, Cows => 2);
Animals(Animal_Sum);
end Default2;
-- Result of Execution
-- Cows = 2 Pigs = 3 Dogs = 4 and they total 9
-- Cows = 2 Pigs = 3 Dogs = 4 and they total 9
-- Cows = 14 Pigs = 20 Dogs = 4 and they total 38
-- Cows = 5 Pigs = 7 Dogs = 0 and they total 12
-- Cows = 14 Pigs = 20 Dogs = 7 and they total 41
-- Cows = 2 Pigs = 3 Dogs = 4 and they total 9
-- Cows = 14 Pigs = 20 Dogs = 0 and they total 34
-- Chapter 18 - Program 3
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Recurson is
Index : INTEGER;
procedure Print_And_Decrement(Value : in INTEGER) is
New_Value : INTEGER;
begin
Put("The value of the index is now");
Put(Value, 3);
New_Line;
New_Value := Value - 1;
if New_Value > 0 then
Print_And_Decrement(New_Value);
end if;
end Print_And_Decrement;
begin
Index := 7;
Print_And_Decrement(Index);
end Recurson;
-- Result of execution
-- The value of the index is now 7
-- The value of the index is now 6
-- The value of the index is now 5
-- The value of the index is now 4
-- The value of the index is now 3
-- The value of the index is now 2
-- The value of the index is now 1
-- Chapter 18 - Program 4
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure FuncRecr is
START : constant := -2;
STOP : constant := 5;
Result : INTEGER;
Data_Value : INTEGER;
function Factorial_Possible(Number : INTEGER) return BOOLEAN;
function Factorial(Number : INTEGER) return INTEGER is
begin
if not Factorial_Possible(Number) then
Put("Factorial not possible for");
Put(Number, 4);
New_Line;
return 0;
end if;
if Number = 0 then
return 1;
elsif Number = 1 then
return 1;
else
return Factorial(Number - 1) * Number;
end if;
end Factorial;
function Factorial_Possible(Number : INTEGER) return BOOLEAN is
begin
if Number >= 0 then
return TRUE;
else
return FALSE;
end if;
end Factorial_Possible;
begin
Put("Factorial program");
New_Line(2);
for Number_To_Try in START..STOP loop
Put(Number_To_Try, 3);
if Factorial_Possible(Number_To_Try) then
Result := Factorial(Number_To_Try);
Put(" is legal to factorialize and the result is");
Put(Result, 6);
else
Put(" is not legal to factorialize.");
end if;
New_Line;
end loop;
New_Line;
Data_Value := 4;
Result := Factorial(2 - Data_Value); -- Factorial(-2)
Result := Factorial(Data_Value + 3); -- Factorial(7)
Result := Factorial(2 * Data_Value - 3); -- Factorial(5)
Result := Factorial(Factorial(3)); -- Factorial(6)
Result := Factorial(4); -- Factorial(4)
Result := Factorial(0); -- Factorial(0)
end FuncRecr;
-- Result of Execution
-- Factorial program
--
-- -2 is not legal to factorialize.
-- -1 is not legal to factorialize.
-- 0 is legal to factorialize and the result is 1
-- 1 is legal to factorialize and the result is 1
-- 2 is legal to factorialize and the result is 2
-- 3 is legal to factorialize and the result is 6
-- 4 is legal to factorialize and the result is 24
-- 5 is legal to factorialize and the result is 120
--
-- Factorial not possible for -2
-- Chapter 18 - Program 5
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Revers is
type MY_ARRAY is array(3..10) of INTEGER;
Store_Here : MY_ARRAY := (3, 16, -5, 6, 12, 66, -13, 57);
Another : MY_ARRAY;
function Reverse_Array(Data : MY_ARRAY) return MY_ARRAY is
Temp : MY_ARRAY;
begin
for Index in Data'RANGE loop
Temp(Index) := Data(Data'FIRST + Data'LAST - Index);
end loop;
return Temp;
end Reverse_Array;
begin
for Index in Store_Here'Range loop
Put(Store_Here(Index), 5);
end loop;
New_Line;
Another := Reverse_Array(Store_Here);
for Index in Another'Range loop
Put(Another(Index), 5);
end loop;
New_Line;
Another := Reverse_Array(Another);
for Index in Another'Range loop
Put(Another(Index), 5);
end loop;
New_Line;
end Revers;
-- Result of Execution
-- 3 16 -5 6 12 66 -13 57
-- 57 -13 66 12 6 -5 16 3
-- 3 16 -5 6 12 66 -13 57
-- Chapter 18 - Program 6
package Shape is
type BOX is
record
Length : INTEGER;
Width : INTEGER;
Height : INTEGER;
end record;
function Make_A_Box(In_Length, In_Width, In_Height : INTEGER)
return BOX;
function "+"(Left, Right : BOX) return BOX;
function "+"(Left : INTEGER; Right : BOX) return BOX;
function "*"(Left : INTEGER; Right : BOX) return BOX;
procedure Print_Box(Input_Box : BOX);
end Shape;
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
package body Shape is
function Make_A_Box(In_Length, In_Width, In_Height : INTEGER)
return BOX is
Temp_Box : BOX;
begin
Temp_Box.Length := In_Length;
Temp_Box.Width := In_Width;
Temp_Box.Height := In_Height;
return Temp_Box;
end Make_A_Box;
function "+"(Left, Right : BOX) return BOX is
Temp_Box : BOX;
begin
Temp_Box.Length := Left.Length + Right.Length;
Temp_Box.Width := Left.Width + Right.Width;
Temp_Box.Height := Left.Height + Right.Height;
return Temp_Box;
end "+";
function "+"(Left : INTEGER; Right : BOX) return BOX is
Temp_Box : BOX;
begin
Temp_Box.Length := Left + Right.Length;
Temp_Box.Width := Left + Right.Width;
Temp_Box.Height := Left + Right.Height;
return Temp_Box;
end "+";
function "*"(Left : INTEGER; Right : BOX) return BOX is
Temp_Box : BOX;
begin
Temp_Box.Length := Left * Right.Length;
Temp_Box.Width := Left * Right.Width;
Temp_Box.Height := Left * Right.Height;
return Temp_Box;
end "*";
procedure Print_Box(Input_Box : BOX) is
begin
Put("Length = ");
Put(Input_Box.Length, 3);
Put(" Width = ");
Put(Input_Box.Width, 3);
Put(" Height = ");
Put(Input_Box.Height, 3);
New_Line;
end Print_Box;
end Shape;
with Shape;
use type Shape.BOX;
procedure OperOver is
Small_Box : Shape.BOX;
Big_Box : Shape.BOX;
begin
Small_Box := Shape.Make_A_Box(2, 3, 2);
Big_Box := Shape.Make_A_Box(4, 5, 3);
Shape.Print_Box(Small_Box);
Shape.Print_Box(Big_Box);
Shape.Print_Box(Small_Box + Big_Box);
Shape.Print_Box(4 + Small_Box);
Shape.Print_Box(10 * Big_Box);
Shape.Print_Box(Small_Box + 5 * Big_Box);
Shape.Print_Box(Shape."+"(Small_Box, Shape."*"(5, Big_Box)));
end OperOver;
-- Result of execution
-- Length = 2 Width = 3 Height = 2
-- Length = 4 Width = 5 Height = 3
-- Length = 6 Width = 8 Height = 5
-- Length = 6 Width = 7 Height = 6
-- Length = 40 Width = 50 Height = 30
-- Length = 22 Width = 28 Height = 17
-- Length = 22 Width = 28 Height = 17
-- Chapter 18 - Programming example 1
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure CH18_1 is
Index : INTEGER;
procedure Print_And_Decrement(Value : in INTEGER) is
New_Value : INTEGER;
begin
Put("The value of the index is now");
Put(Value, 6);
New_Line;
New_Value := Value - 1;
if New_Value > 0 then
Print_And_Decrement(New_Value);
end if;
Put("Following the recursive call, value =");
Put(Value, 6);
New_Line;
end Print_And_Decrement;
begin
Index := 7;
Print_And_Decrement(Index);
end CH18_1;
-- Result of execution
-- The value of the index is now 7
-- The value of the index is now 6
-- The value of the index is now 5
-- The value of the index is now 4
-- The value of the index is now 3
-- The value of the index is now 2
-- The value of the index is now 1
-- Following the recursive call, value = 1
-- Following the recursive call, value = 2
-- Following the recursive call, value = 3
-- Following the recursive call, value = 4
-- Following the recursive call, value = 5
-- Following the recursive call, value = 6
-- Following the recursive call, value = 7
-- Chapter 18 - Programming exercise 2
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure CH18_2 is
Data_Point : INTEGER;
Procedure Increment(In_Val : in out INTEGER);
procedure Display(Dis_Val : in out INTEGER) is
begin
Put("The value of the variable is now ");
Put(Dis_Val);
New_Line;
Increment(Dis_Val);
end Display;
procedure Increment(In_Val : in out INTEGER) is
begin
if In_Val < 8 then
In_Val := In_Val + 1;
Display(In_Val);
end if;
end Increment;
begin
Data_Point := 1;
Increment(Data_Point);
end CH18_2;
-- result of execution
-- The value of the variable is now 2
-- The value of the variable is now 3
-- The value of the variable is now 4
-- The value of the variable is now 5
-- The value of the variable is now 6
-- The value of the variable is now 7
-- The value of the variable is now 8
-- Chapter 19 - Program 1
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Summer is
type MY_ARRAY is array (POSITIVE range <>) of INTEGER;
Dummy1 : constant MY_ARRAY := (27, 14, 13, 33);
Dummy2 : constant MY_ARRAY := (112 => 27, 113 => 14,
114 => 13, 115 => 33);
My_List : MY_ARRAY(1..12);
Stuff : MY_ARRAY(4..11) := (12, 13, 7, 11, 125, others => 17);
Total : INTEGER;
function Sum_Up(In_Array : MY_ARRAY) return INTEGER is
Sum : INTEGER := 0;
begin
for Index in In_Array'FIRST..In_Array'LAST loop
Sum := Sum + In_Array(Index);
end loop;
Put("The sum of the numbers is");
Put(Sum, 4);
New_Line;
return Sum;
end Sum_Up;
begin
My_List := (0, 1, 2, 3, 4, 3, 2, 1, 2, 3, 4, 7);
Stuff := (4 => 12, 8 => 11, 5 => 13, 7 => 1, others => 9);
Total := Sum_Up(My_List);
Total := Sum_Up(Stuff);
end Summer;
-- Result of Execution
-- The sum of the numbers is 32
-- The sum of the numbers is 73
-- Chapter 19 - Program 2
with Ada.Text_IO, Ada. Float_Text_IO;
use Ada.Text_IO, Ada.Float_Text_IO;
procedure EnumAry is
type DAY is (MON, TUE, WED, THU, FRI, SAT, SUN);
Hours : array(DAY) of FLOAT;
Total_Hours : FLOAT;
Today : DAY;
begin
for Today in MON..FRI loop
Hours(Today) := 8.0;
end loop;
Hours(SAT) := 4.0;
Hours(SUN) := 0.0;
Total_Hours := 0.0;
for Today in DAY loop
Total_Hours := Total_Hours + Hours(Today);
end loop;
Put("Total hours for the week =");
Put(Total_Hours, 5, 2, 0);
New_Line;
end EnumAry;
-- Result of Execution
-- Total hours for the week = 44.00
-- Chapter 19 - Program 3
with Ada.Text_IO;
use Ada.Text_IO;
procedure ArrayOps is
type ARY_INT is array(1..6) of INTEGER;
type ARY_BOOL is array(4..7) of BOOLEAN;
Do_They_Compare : BOOLEAN;
Crowd, Group1, Group2 : ARY_INT;
Result, Answer1, Answer2 : ARY_BOOL;
begin
Group1 := (12, 17, -1, 3, -100, 5);
Group2 := (13, -2, 22, 1, 1242, -12);
Do_They_Compare := Group1 <= Group2;
Do_They_Compare := Group1 > Group2;
if Group1 = Group2 then
Put("The arrays are equal."); New_Line;
end if;
-- Crowd := Group1 + Group2;
-- Crowd := Group1 - Group2;
-- Crowd := Group1 * Group2;
-- Crowd := Group1 / Group2;
-- Crowd := Group1 mod Group2;
-- Crowd := Group1 rem Group2;
Answer1 := (TRUE, FALSE, TRUE, FALSE);
Answer2 := (TRUE, FALSE, FALSE, TRUE);
Result := Answer1 and Answer2;
Result := not Answer2;
Result := Answer1 or Answer2;
Result := Answer1 xor Answer2;
if Answer1 /= Answer2 then
Put("The BOOLEAN arrays are not equal.");
New_Line;
end if;
end ArrayOps;
-- Result of execution
-- The BOOLEAN arrays are not equal.
-- Chapter 19 - Program 4
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure ArrayOp2 is
type ARY_INT is array(1..6) of INTEGER;
Crowd, Group1, Group2 : ARY_INT;
function "+"(In_Array1, In_Array2 : ARY_INT) return ARY_INT is
Temp_Array : ARY_INT;
begin
for Index in ARY_INT'RANGE loop
Temp_Array(Index) := In_Array1(Index) + In_Array2(Index);
end loop;
return Temp_Array;
end "+";
function "mod"(In_Array1, In_Array2 : ARY_INT) return ARY_INT is
Temp_Array : ARY_INT;
begin
for Index in ARY_INT'RANGE loop
Temp_Array(Index) := In_Array1(Index) mod In_Array2(Index);
end loop;
return Temp_Array;
end "mod";
begin
Group1 := (12, 17, -1, 3, -100, 5);
Group2 := (13, -2, 22, 1, 1242, -12);
Crowd := Group1 + Group2;
for Index in ARY_INT'RANGE loop
Put(Group1(Index), 6);
Put(Group2(Index), 6);
Put(Crowd(Index), 6);
New_Line;
end loop;
-- Crowd := Group1 - Group2;
-- Crowd := Group1 * Group2;
-- Crowd := Group1 / Group2;
Crowd := Group1 mod Group2;
-- Crowd := Group1 rem Group2;
end ArrayOp2;
-- Result of execution
-- 12 13 25
-- 17 -2 15
-- -1 22 21
-- 3 1 4
-- -100 1242 1142
-- 5 -12 -7
-- Chapter 19 - Program 5
procedure UnaryOp is
type ARY_INT is array(1..6) of INTEGER;
Crowd, Group1, Group2 : ARY_INT;
function "+"(In_Array1, In_Array2 : ARY_INT) return ARY_INT is
Temp_Array : ARY_INT;
begin
for Index in ARY_INT'RANGE loop
Temp_Array(Index) := In_Array1(Index) + In_Array2(Index);
end loop;
return Temp_Array;
end "+";
function "-"(In_Array1, In_Array2 : ARY_INT) return ARY_INT is
Temp_Array : ARY_INT;
begin
for Index in ARY_INT'RANGE loop
Temp_Array(Index) := In_Array1(Index) - In_Array2(Index);
end loop;
return Temp_Array;
end "-";
function "+"(In_Array : ARY_INT) return ARY_INT is
begin
return In_Array;
end "+";
function "-"(In_Array : ARY_INT) return ARY_INT is
Temp_Array : ARY_INT;
begin
for Index in ARY_INT'RANGE loop
Temp_Array(Index) := - In_Array(Index);
end loop;
return Temp_Array;
end "-";
begin
Group1 := (12, 17, -1, 3, -100, 5);
Group2 := (13, -2, 22, 1, 1242, -12);
Crowd := Group1 + Group2;
Crowd := Group1 - Group2;
Crowd := +Group1;
Crowd := -Group1;
Crowd := (Group1 + Group2) - (-Group1 + Group2);
end UnaryOp;
-- Result of execution
-- (There is no output from this program)
-- Chapter 19 - Programming exercise 1
with Ada.Text_IO, Ada.Float_Text_IO;
use Ada.Text_IO, Ada.Float_Text_IO;
procedure CH19_1 is
type DAY is (MON,TUE,WED,THU,FRI,SAT,SUN);
package Enum_IO is new Ada.Text_IO.Enumeration_IO(DAY);
use Enum_IO;
Hours : array(DAY) of FLOAT;
Total_Hours : FLOAT;
Today : DAY;
begin
for Today in MON..FRI loop
Hours(Today) := 8.0;
end loop;
Hours(SAT) := 4.0;
Hours(SUN) := 0.0;
Total_Hours := 0.0;
for Today in DAY loop
Total_Hours := Total_Hours + Hours(Today);
Put(Hours(Today), 4, 2, 0);
Put(" hours were worked on ");
Put(Today);
New_Line;
end loop;
Put("Total hours for the week =");
Put(Total_Hours, 8, 2, 0);
New_Line;
end CH19_1;
-- Result of Execution
-- 8.00 hours were worked on MON
-- 8.00 hours were worked on TUE
-- 8.00 hours were worked on WED
-- 8.00 hours were worked on THU
-- 8.00 hours were worked on FRI
-- 4.00 hours were worked on SAT
-- 0.00 hours were worked on SUN
-- Total hours for the week = 44.00
-- Chapter 19 - Programming exercise 2
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure CH19_2 is
type ARY_INT is array(1..6) of INTEGER;
Crowd, Group1, Group2 : ARY_INT;
function "+"(In_Array1, In_Array2 : ARY_INT) return ARY_INT is
Temp_Array : ARY_INT;
begin
for Index in ARY_INT'RANGE loop
Temp_Array(Index) := In_Array1(Index) + In_Array2(Index);
end loop;
return Temp_Array;
end "+";
function "-"(In_Array1, In_Array2 : ARY_INT) return ARY_INT is
Temp_Array : ARY_INT;
begin
for Index in ARY_INT'RANGE loop
Temp_Array(Index) := In_Array1(Index) - In_Array2(Index);
end loop;
return Temp_Array;
end "-";
function "*"(In_Array1, In_Array2 : ARY_INT) return ARY_INT is
Temp_Array : ARY_INT;
begin
for Index in ARY_INT'RANGE loop
Temp_Array(Index) := In_Array1(Index) * In_Array2(Index);
end loop;
return Temp_Array;
end "*";
function "/"(In_Array1, In_Array2 : ARY_INT) return ARY_INT is
Temp_Array : ARY_INT;
begin
for Index in ARY_INT'RANGE loop
Temp_Array(Index) := In_Array1(Index) / In_Array2(Index);
end loop;
return Temp_Array;
end "/";
function "mod"(In_Array1, In_Array2 : ARY_INT) return ARY_INT is
Temp_Array : ARY_INT;
begin
for Index in ARY_INT'RANGE loop
Temp_Array(Index) := In_Array1(Index) mod In_Array2(Index);
end loop;
return Temp_Array;
end "mod";
function "rem"(In_Array1, In_Array2 : ARY_INT) return ARY_INT is
Temp_Array : ARY_INT;
begin
for Index in ARY_INT'RANGE loop
Temp_Array(Index) := In_Array1(Index) rem In_Array2(Index);
end loop;
return Temp_Array;
end "rem";
begin
Group1 := (12, 17, -1, 3, -100, 5);
Group2 := (13, -2, 22, 1, 124, -12);
Crowd := Group1 + Group2;
for Index in ARY_INT'RANGE loop
Put(Group1(Index), 6);
Put(Group2(Index), 6);
Put(Crowd(Index), 6);
New_Line;
end loop;
Crowd := Group1 - Group2;
Crowd := Group1 * Group2;
Crowd := Group1 / Group2;
Crowd := Group1 mod Group2;
Crowd := Group1 rem Group2;
end CH19_2;
-- Result of execution
-- 12 13 25
-- 17 -2 15
-- -1 22 21
-- 3 1 4
-- -100 124 24
-- 5 -12 -7
-- Note; The fifth value of Group2 was changed to prevent a
-- numeric_error on a small machine.
-- Chapter 20 - Program 1
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Discrim1 is
type SQUARE is array(INTEGER range <>,
INTEGER range <>) of INTEGER;
type LINEAR_TYPE is array(INTEGER range <>) of POSITIVE;
type STUFF(List_Size : POSITIVE) is
record
Matrix : SQUARE(1..List_Size, 1..List_Size);
Elements : INTEGER := List_Size * List_Size;
Linear : LINEAR_TYPE(1..List_Size);
Number : INTEGER := List_Size;
end record;
type ANOTHER_STUFF is new STUFF;
subtype STUFF_5 is STUFF(5);
Data_Store : STUFF(5);
Big_Store : STUFF(12);
Extra_Store : ANOTHER_STUFF(5);
More_Store : STUFF(5);
Five_Store : STUFF_5;
Name_Store : STUFF(List_Size => 5);
begin
for Index1 in Data_Store.Matrix'RANGE(1) loop
Data_Store.Linear(Index1) := Index1;
for Index2 in Data_Store.Matrix'RANGE(2) loop
Data_Store.Matrix(Index1, Index2) := Index1 * Index2;
end loop;
end loop;
Five_Store := Data_Store;
More_Store := Five_Store;
Put("The number of elements in More_Store.Matrix is");
Put(More_Store.Elements, 5);
New_Line;
end Discrim1;
-- Result of execution
-- The number of elements in More_Store.Matrix is 25
-- Chapter 20 - Program 2
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Discrim2 is
type SQUARE is array(INTEGER range <>,
INTEGER range <>) of INTEGER;
type LINEAR_TYPE is array(INTEGER range <>) of POSITIVE;
type STUFF(List_Size : POSITIVE) is
record
Matrix : SQUARE(1..List_Size, 1..List_Size);
Elements : INTEGER := List_Size * List_Size;
Linear : LINEAR_TYPE(1..List_Size);
Number : INTEGER := List_Size;
end record;
Data_Store : STUFF(5);
Big_Store : STUFF(12);
function Add_Elements(In_Array : STUFF) return INTEGER is
Total : INTEGER := 0;
begin
for Index1 in In_Array.Matrix'RANGE(1) loop
for Index2 in In_Array.Matrix'RANGE(2) loop
Total := Total + In_Array.Matrix(Index1, Index2);
end loop;
end loop;
return Total;
end Add_Elements;
procedure Set_To_Ones(Work_Array : in out STUFF) is
begin
for Index1 in Work_Array.Matrix'RANGE(1) loop
for Index2 in Work_Array.Matrix'RANGE(2) loop
Work_Array.Matrix(Index1, Index2) := 1;
end loop;
end loop;
end Set_To_Ones;
begin
for Index1 in 1..Data_Store.List_Size loop
Data_Store.Linear(Index1) := Index1;
for Index2 in 1..Data_Store.List_Size loop
Data_Store.Matrix(Index1, Index2) := Index1 * Index2;
end loop;
end loop;
Set_To_Ones(Big_Store);
Put("The total of Data_Store is");
Put(Add_Elements(Data_Store), 5);
New_Line;
Put("The total of Big_Store is ");
Put(Add_Elements(Big_Store), 5);
New_Line;
end Discrim2;
-- Result of execution
-- The total of Data_Store is 225
-- The total of Big_Store is 144
-- Chapter 20 - Program 3
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Discrim3 is
type SQUARE is array(INTEGER range <>,
INTEGER range <>) of INTEGER;
type LINEAR_TYPE is array(INTEGER range <>) of POSITIVE;

subtype SMALL_POS is POSITIVE range 1..20;
type STUFF(List_Size : SMALL_POS := 2) is
record
Matrix : SQUARE(1..List_Size, 1..List_Size);
Elements : INTEGER := List_Size * List_Size;
Linear : LINEAR_TYPE(1..List_Size);
Number : INTEGER := List_Size;
end record;
Data_Store : STUFF(5);
Big_Store : STUFF(12);
Var_Store : STUFF;
function Add_Elements(In_Array : STUFF) return INTEGER is
Total : INTEGER := 0;
begin
for Index1 in In_Array.Matrix'RANGE(1) loop
for Index2 in In_Array.Matrix'RANGE(2) loop
Total := Total + In_Array.Matrix(Index1, Index2);
end loop;
end loop;
return Total;
end Add_Elements;
procedure Set_To_Ones(Work_Array : in out STUFF) is
begin
for Index1 in Work_Array.Matrix'RANGE(1) loop
for Index2 in Work_Array.Matrix'RANGE(2) loop
Work_Array.Matrix(Index1, Index2) := 1;
end loop;
end loop;
end Set_To_Ones;
begin
for Index1 in 1..Data_Store.List_Size loop
Data_Store.Linear(Index1) := Index1;
for Index2 in 1..Data_Store.List_Size loop
Data_Store.Matrix(Index1, Index2) := Index1 * Index2;
end loop;
end loop;
Var_Store := Data_Store;
Put("The total of Var_Store is ");
Put(Add_Elements(Var_Store), 4);
New_Line;
Set_To_Ones(Var_Store);
Put("The total of Var_Store is ");
Put(Add_Elements(Var_Store), 4);
New_Line;
Set_To_Ones(Big_Store);
Var_Store := Big_Store;
Put("The total of Var_Store is ");
Put(Add_Elements(Var_Store), 4);
New_Line;
end Discrim3;
-- Result of execution
-- The total of Var_Store is 225
-- The total of Var_Store is 25
-- The total of Var_Store is 144
-- Chapter 20 - Program 4
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Discrim4 is
type SQUARE is array(INTEGER range <>,
INTEGER range <>) of INTEGER;
type LINEAR_TYPE is array(INTEGER range <>) of POSITIVE;
subtype SMALL_POS is POSITIVE range 1..20;
type STUFF(List_Length : SMALL_POS := 3;
List_Width : SMALL_POS := 5;
Linear_Size : SMALL_POS := 7) is
record
Matrix : SQUARE(1..List_Length, 1..List_Width);
Elements : INTEGER := List_Length * List_Width;
Linear : LINEAR_TYPE(1..Linear_Size);
Number : INTEGER := Linear_Size;
end record;
Data_Store : STUFF(5, 5, 5);
Big_Store : STUFF(12, 12, 12);
Different : STUFF(5, 7, 4);
More_Store : STUFF(Linear_Size => 15,
List_Length => 6,
List_Width => 2);
Variable : STUFF;
function Add_Elements(In_Array : STUFF) return INTEGER is
Total : INTEGER := 0;
begin
for Index1 in In_Array.Matrix'RANGE(1) loop
for Index2 in In_Array.Matrix'RANGE(2) loop
Total := Total + In_Array.Matrix(Index1, Index2);
end loop;
end loop;
return Total;
end Add_Elements;
procedure Set_To_Ones(Work_Array : in out STUFF) is
begin
for Index1 in Work_Array.Matrix'RANGE(1) loop
for Index2 in Work_Array.Matrix'RANGE(2) loop
Work_Array.Matrix(Index1, Index2) := 1;
end loop;
end loop;
end Set_To_Ones;
begin
for Index1 in 1..Data_Store.List_Length loop
Data_Store.Linear(Index1) := Index1;
for Index2 in 1..Data_Store.List_Width loop
Data_Store.Matrix(Index1, Index2) := Index1 * Index2;
end loop;
end loop;
Variable := Data_Store;
Put("The total of Variable is");
Put(Add_Elements(Variable), 6);
New_Line;
Variable := More_Store;
Set_To_Ones(Big_Store);
Set_To_Ones(Different);
Set_To_Ones(More_Store);
Set_To_Ones(Variable);
Put("The total of Variable is");
Put(Add_Elements(Variable), 6);
New_Line;
Variable := Different;
Put("The total of Variable is");
Put(Add_Elements(Variable), 6);
New_Line;
end Discrim4;
-- Result of execution
-- The total of Variable is 225
-- The total of Variable is 12
-- The total of Variable is 35
-- Chapter 20 - Program 5
procedure Variant1 is
type POWER is (GAS, STEAM, DIESEL, NONE);
type VEHICLE (Engine : POWER) is
record
Model_Year : INTEGER range 1888..1992;
Wheels : INTEGER range 2..18;
case Engine is
when GAS => Cylinders : INTEGER range 1..16;
when STEAM => Boiler_Size : INTEGER range 5..22;
Coal_Burner : BOOLEAN;
when DIESEL => Fuel_Inject : BOOLEAN;
when NONE => Speeds : INTEGER range 1..15;
end case;
end record;
Ford : VEHICLE(GAS);
Truck : VEHICLE(DIESEL);
Schwinn : VEHICLE(NONE);
Stanley : VEHICLE(STEAM);
begin
Ford.Model_Year := 1956; -- Component assignment
Ford.Wheels := 4;
Ford.Cylinders := 8;
Ford := (GAS, 1956, 4, 8); -- Positional aggregate assignment
-- Named aggregate assignment
Ford := (Model_Year => 1956, Cylinders => 8, Engine => GAS,
Wheels => 4);
Stanley.Model_Year := 1908;
Stanley.Wheels := 4;
Stanley.Boiler_Size := 21;
Stanley.Coal_Burner := FALSE;
-- Mixed aggregate assignment
Stanley := (STEAM, 1908, 4, Coal_Burner => FALSE,
Boiler_Size => 21);
Schwinn.Speeds := 10;
Schwinn.Wheels := 2;
Schwinn.Model_Year := 1985;
Truck.Model_Year := 1966;
Truck.Wheels := 18;
Truck.Fuel_Inject := TRUE;
end Variant1;
-- Result of Execution
-- (No results are output.)
-- Chapter 20 - Program 6
procedure Variant2 is
type POWER is (GAS, STEAM, DIESEL, NONE);
type VEHICLE (Engine : POWER := NONE) is
record
Model_Year : INTEGER range 1888..1992;
Wheels : INTEGER range 2..18;
case Engine is
when GAS => Cylinders : INTEGER range 1..16;
when STEAM => Boiler_Size : INTEGER range 5..22;
Coal_Burner : BOOLEAN;
when DIESEL => Fuel_Inject : BOOLEAN;
when NONE => Speeds : INTEGER range 1..15;
end case;
end record;
Ford, Truck, Schwinn : VEHICLE;
Stanley : VEHICLE(STEAM);
begin
Ford := (GAS, 1956, 4, 8);
Ford := (DIESEL, 1985, Fuel_Inject => TRUE, Wheels => 8);
Truck := (DIESEL, 1966, 18, TRUE);
Truck.Model_Year := 1968;
Truck.Fuel_Inject := FALSE;
Stanley.Model_Year := 1908; -- This is constant as STEAM
Stanley.Wheels := 4;
Stanley.Boiler_Size := 21;
Stanley.Coal_Burner := FALSE;
Schwinn.Speeds := 10; -- This defaults to NONE
Schwinn.Wheels := 2;
Schwinn.Model_Year := 1985;
end Variant2;
-- Result of Execution
-- (No output when executed)
-- Chapter 20 - Program 7
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Infix is
type THREE_INTS is
record
Length, Height, Width : INTEGER;
end record;
Big, Medium, Sum : THREE_INTS;
function "+"(Record1, Record2 : THREE_INTS) return THREE_INTS is
Temp : THREE_INTS;
begin
Temp.Length := Record1.Length + Record2.Length;
Temp.Height := Record1.Height + Record2.Height;
Temp.Width := Record1.Width + Record2.Width;
return Temp;
end "+";
begin
Big.Length := 10 + 2;
Big.Height := 22;
Big.Width := 17;
Medium.Length := 5;
Medium.Height := 7;
Medium.Width := 4;
Sum := Big + Medium;
Put("The sum is");
Put(Sum.Length, 4);
Put(Sum.Height, 4);
Put(Sum.Width, 4);
New_Line;
end Infix;
-- Result of Execution
-- The sum is 17 29 21
-- Chapter 20 - Programming exercise 1
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure CH20_1 is
type POWER is (GAS, STEAM, DIESEL, NONE);
package Enum_IO is new Ada.Text_IO.Enumeration_IO(POWER);
use Enum_IO;
type VEHICLE (Engine : POWER) is
record
Model_Year : INTEGER range 1888..1992;
Wheels : INTEGER range 2..18;
case Engine is
when GAS => Cylinders : INTEGER range 1..16;
when STEAM => Boiler_Size : INTEGER range 5..22;
Coal_Burner : BOOLEAN;
when DIESEL => Fuel_Inject : BOOLEAN;
when NONE => Speeds : INTEGER range 1..15;
end case;
end record;
Ford : VEHICLE(GAS);
Truck : VEHICLE(DIESEL);
Schwinn : VEHICLE(NONE);
Stanley : VEHICLE(STEAM);
begin
Ford.Model_Year := 1956; -- Component assignment
Ford.Wheels := 4;
Ford.Cylinders := 8;
Ford := (GAS, 1956, 4, 8); -- Positional aggregate assignment
-- Named aggregate assignment
Ford := (Model_Year => 1956, Cylinders => 8, Engine => GAS,
Wheels => 4);
Stanley.Model_Year := 1908;
Stanley.Wheels := 4;
Stanley.Boiler_Size := 21;
Stanley.Coal_Burner := FALSE;
-- Mixed aggregate assignment
Stanley := (STEAM, 1908, 4, Coal_Burner => FALSE,
Boiler_Size => 21);
Schwinn.Speeds := 10;
Schwinn.Wheels := 2;
Schwinn.Model_Year := 1985;
Truck.Model_Year := 1966;
Truck.Wheels := 18;
Truck.Fuel_Inject := TRUE;
Put("Stanley engine type is ");
Put(Stanley.Engine);
Put(", and has");
Put(Stanley.Wheels,3);
Put_Line(" wheels.");
Put("The truck is a");
Put(Truck.Model_Year,5);
Put(" and has");
Put(Truck.Wheels,3);
Put_Line(" wheels.");
end CH20_1;
-- Result of Execution
-- Stanley engine type is STEAM, and has 4 wheels.
-- The truck is a 1966 and has 18 wheels.
-- Chapter 20 - Programming exercise 2
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure CH20_2 is
type POWER is (GAS, STEAM, DIESEL, NONE);
type VEHICLE (Engine : POWER := NONE) is
record
Model_Year : INTEGER range 1888..1992;
Wheels : INTEGER range 2..18;
case Engine is
when GAS => Cylinders : INTEGER range 1..16;
when STEAM => Boiler_Size : INTEGER range 5..22;
Coal_Burner : BOOLEAN;
when DIESEL => Fuel_Inject : BOOLEAN;
when NONE => Speeds : INTEGER range 1..15;
end case;
end record;
Ford, Truck, Schwinn : VEHICLE;
Stanley : VEHICLE(STEAM);
begin
Ford := (GAS, 1956, 4, 8);
Ford := (DIESEL, 1985, Fuel_Inject => TRUE, Wheels => 8);
Truck := (DIESEL, 1966, 18, TRUE);
Truck.Model_Year := 1968;
Truck.Fuel_Inject := FALSE;
Stanley.Model_Year := 1908; -- This is constant as STEAM
Stanley.Wheels := 4;
Stanley.Boiler_Size := 21;
Stanley.Coal_Burner := FALSE;
Stanley := (DIESEL, 1966, 18, TRUE); -- This is an error
Schwinn.Speeds := 10; -- This defaults to NONE
Schwinn.Wheels := 2;
Schwinn.Model_Year := 1985;
end CH20_2;
-- Result of Execution
-- (You will probably get a compiler warning stating that there
-- is a constraint error. During runtime, you will get the error
-- stating that there is a length or discriminant clash.)
-- Chapter 20 - Programming exercise 3
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure CH20_3 is
type THREE_INTS is
record
Length, Height, Width : INTEGER;
end record;
Big, Medium, Sum : THREE_INTS;
Grand_Total : INTEGER;
function "+"(Record1, Record2 : THREE_INTS) return THREE_INTS is
Temp : THREE_INTS;
begin
Temp.Length := Record1.Length + Record2.Length;
Temp.Height := Record1.Height + Record2.Height;
Temp.Width := Record1.Width + Record2.Width;
return Temp;
end "+";
function "+"(Record1, Record2 : THREE_INTS) return INTEGER is
Total : INTEGER;
begin
Total := Record1.Length + Record2.Length +
Record1.Height + Record2.Height +
Record1.Width + Record2.Width;
return Total;
end "+";
begin
Big.Length := 10 + 2;
Big.Height := 22;
Big.Width := 17;
Medium.Length := 5;
Medium.Height := 7;
Medium.Width := 4;
Sum := Big + Medium;
Put("The sum is");
Put(Sum.Length, 5);
Put(Sum.Height, 5);
Put(Sum.Width, 5);
New_Line;
Grand_Total := Big + Medium;
Put("The grand total is ");
Put(Grand_Total, 5);
New_Line;
end CH20_3;
-- Result of Execution
-- The sum is 17 29 21
-- The grand total is 67
-- Chapter 21 - Program 1
-- This package uses a data structure composed of three INTEGER
-- variables. It allow the user to add two structures, component
-- by component, or subtract component by component. Provision is
-- also made to build a structure from three numbers, or decompose
-- a structure into its components.
package Three is
type DATA_STRUCTURE is
record
Value1 : INTEGER;
Value2 : INTEGER;
Value3 : INTEGER;
end record;
function "+"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE;
function "-"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE;
function Build_Structure(Val1, Val2, Val3 : INTEGER) return
DATA_STRUCTURE;
procedure Decompose(Data1 : DATA_STRUCTURE;
Val1, Val2, Val3 : out INTEGER);
end Three;
package body Three is
function "+"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE is
Temp : DATA_STRUCTURE;
begin
Temp.Value1 := Data1.Value1 + Data2.Value1;
Temp.Value2 := Data1.Value2 + Data2.Value2;
Temp.Value3 := Data1.Value3 + Data2.Value3;
return Temp;
end "+";
function "-"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE is
Temp : DATA_STRUCTURE;
begin
Temp.Value1 := Data1.Value1 - Data2.Value1;
Temp.Value2 := Data1.Value2 - Data2.Value2;
Temp.Value3 := Data1.Value3 - Data2.Value3;
return Temp;
end "-";
function Build_Structure(Val1, Val2, Val3 : INTEGER) return
DATA_STRUCTURE is
Temp : DATA_STRUCTURE;
begin
Temp.Value1 := Val1;
Temp.Value2 := Val2;
Temp.Value3 := Val3;
return Temp;
end Build_Structure;
procedure Decompose(Data1 : DATA_STRUCTURE;
Val1, Val2, Val3 : out INTEGER) is
begin
Val1 := Data1.Value1;
Val2 := Data1.Value2;
Val3 := Data1.Value3;
end Decompose;
end Three;
-- This program exercises the package Three as an illustration.
with Ada.Text_IO; use Ada.Text_IO;
with Three; use Three;
procedure NoPrivat is
My_Data, Extra_Data : DATA_STRUCTURE;
begin
My_Data := Build_Structure(3, 7, 13);
Extra_Data := Build_Structure(-4, 77, 0);
My_Data := My_Data + Extra_Data;
if My_Data /= Extra_Data then
Put_Line("The two structures are not equal.");
end if;
My_Data := Extra_Data;
if My_Data = Extra_Data then
Put_Line("The two structures are equal now.");
end if;
My_Data.Value1 := My_Data.Value1 + 13;
end NoPrivat;
-- Result of execution
-- The two structures are not equal.
-- The two structures are equal now.
-- Chapter 21 - Program 2
-- This package uses a data structure composed of three INTEGER
-- variables. It allow the user to add two structures, component
-- by component, or subtract component by component. Provision is
-- also made to build a structure from three numbers, or decompose
-- a structure into its components.
package Three is
type DATA_STRUCTURE is private;
function "+"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE;
function "-"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE;
function Build_Structure(Val1, Val2, Val3 : INTEGER) return
DATA_STRUCTURE;
procedure Decompose(Data1 : DATA_STRUCTURE;
Val1, Val2, Val3 : out INTEGER);
private
type DATA_STRUCTURE is
record
Value1 : INTEGER;
Value2 : INTEGER;
Value3 : INTEGER;
end record;
end Three;
package body Three is
function "+"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE is
Temp : DATA_STRUCTURE;
begin
Temp.Value1 := Data1.Value1 + Data2.Value1;
Temp.Value2 := Data1.Value2 + Data2.Value2;
Temp.Value3 := Data1.Value3 + Data2.Value3;
return Temp;
end "+";
function "-"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE is
Temp : DATA_STRUCTURE;
begin
Temp.Value1 := Data1.Value1 - Data2.Value1;
Temp.Value2 := Data1.Value2 - Data2.Value2;
Temp.Value3 := Data1.Value3 - Data2.Value3;
return Temp;
end "-";
function Build_Structure(Val1, Val2, Val3 : INTEGER) return
DATA_STRUCTURE is
Temp : DATA_STRUCTURE;
begin
Temp.Value1 := Val1;
Temp.Value2 := Val2;
Temp.Value3 := Val3;
return Temp;
end Build_Structure;
procedure Decompose(Data1 : DATA_STRUCTURE;
Val1, Val2, Val3 : out INTEGER) is
begin
Val1 := Data1.Value1;
Val2 := Data1.Value2;
Val3 := Data1.Value3;
end Decompose;
end Three;
-- This program exercises the package Three as an illustration.
with Ada.Text_IO; use Ada.Text_IO;
with Three; use Three;
procedure Privat1 is
My_Data, Extra_Data : DATA_STRUCTURE;
Temp : DATA_STRUCTURE;
begin
My_Data := Build_Structure(3, 7, 13);
Extra_Data := Build_Structure(-4, 77, 0);
My_Data := My_Data + Extra_Data;
if My_Data /= Extra_Data then
Put_Line("The two structures are not equal.");
end if;
My_Data := Extra_Data;
if My_Data = Extra_Data then
Put_Line("The two structures are equal now.");
end if;
-- The following line is illegal with the private type.
-- My_Data.Value1 := My_Data.Value1 + 13;
Temp := Build_Structure(13, 0, 0);
My_Data := My_Data + Temp;
end Privat1;
-- Result of execution
-- The two structures are not equal.
-- The two structures are equal now.
-- Chapter 21 - Program 3
-- This package uses a data structure composed of three INTEGER
-- variables. It allow the user to add two structures, component
-- by component, or subtract component by component. Provision is
-- also made to build a structure from three numbers, or decompose
-- a structure into its components.
package Three is
type DATA_STRUCTURE is private;
function "+"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE;
function "-"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE;
function Build_Structure(Val1, Val2, Val3 : INTEGER) return
DATA_STRUCTURE;
procedure Decompose(Data1 : DATA_STRUCTURE;
Val1, Val2, Val3 : out INTEGER);
private
type DATA_STRUCTURE is array(1..3) of INTEGER;
end Three;
package body Three is
function "+"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE is
Temp : DATA_STRUCTURE;
begin
Temp(1) := Data1(1) + Data2(1);
Temp(2) := Data1(2) + Data2(2);
Temp(3) := Data1(3) + Data2(3);
return Temp;
end "+";
function "-"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE is
Temp : DATA_STRUCTURE;
begin
Temp(1) := Data1(1) - Data2(1);
Temp(2) := Data1(2) - Data2(2);
Temp(3) := Data1(3) - Data2(3);
return Temp;
end "-";
function Build_Structure(Val1, Val2, Val3 : INTEGER) return
DATA_STRUCTURE is
Temp : DATA_STRUCTURE;
begin
Temp(1) := Val1;
Temp(2) := Val2;
Temp(3) := Val3;
return Temp;
end Build_Structure;
procedure Decompose(Data1 : DATA_STRUCTURE;
Val1, Val2, Val3 : out INTEGER) is
begin
Val1 := Data1(1);
Val2 := Data1(2);
Val3 := Data1(3);
end Decompose;
end Three;
-- This program exercises the package Three as an illustration.
with Ada.Text_IO; use Ada.Text_IO;
with Three; use Three;
procedure Privat2 is
My_Data, Extra_Data : DATA_STRUCTURE;
Temp : DATA_STRUCTURE;
begin
My_Data := Build_Structure(3, 7, 13);
Extra_Data := Build_Structure(-4, 77, 0);
My_Data := My_Data + Extra_Data;
if My_Data /= Extra_Data then
Put_Line("The two structures are not equal.");
end if;
My_Data := Extra_Data;
if My_Data = Extra_Data then
Put_Line("The two structures are equal now.");
end if;
-- The following line is illegal with the private type.
-- My_Data.Value1 := My_Data.Value1 + 13;
Temp := Build_Structure(13, 0, 0);
My_Data := My_Data + Temp;
end Privat2;
-- Result of execution
-- The two structures are not equal.
-- The two structures are equal now.
-- Chapter 21 - Program 4
-- This package uses a data structure composed of three INTEGER
-- variables. It allow the user to add two structures, component
-- by component, or subtract component by component. Provision is
-- also made to build a structure from three numbers, or decompose
-- a structure into its components.
package Three is
type DATA_STRUCTURE is limited private;
function "+"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE;
function "-"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE;
function Build_Structure(Val1, Val2, Val3 : INTEGER) return
DATA_STRUCTURE;
procedure Decompose(Data1 : DATA_STRUCTURE;
Val1, Val2, Val3 : out INTEGER);
procedure Assign_Struct(Data1 : out DATA_STRUCTURE;
Data2 : in DATA_STRUCTURE);
function Compare(Data1, Data2 : DATA_STRUCTURE) return BOOLEAN;
private
type DATA_STRUCTURE is
record
Value1 : INTEGER;
Value2 : INTEGER;
Value3 : INTEGER;
end record;
end Three;
package body Three is
function "+"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE is
Temp : DATA_STRUCTURE;
begin
Temp.Value1 := Data1.Value1 + Data2.Value1;
Temp.Value2 := Data1.Value2 + Data2.Value2;
Temp.Value3 := Data1.Value3 + Data2.Value3;
return Temp;
end "+";
function "-"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE is
Temp : DATA_STRUCTURE;
begin
Temp.Value1 := Data1.Value1 - Data2.Value1;
Temp.Value2 := Data1.Value2 - Data2.Value2;
Temp.Value3 := Data1.Value3 - Data2.Value3;
return Temp;
end "-";
function Build_Structure(Val1, Val2, Val3 : INTEGER) return
DATA_STRUCTURE is
Temp : DATA_STRUCTURE;
begin
Temp.Value1 := Val1;
Temp.Value2 := Val2;
Temp.Value3 := Val3;
return Temp;
end Build_Structure;
procedure Decompose(Data1 : DATA_STRUCTURE;
Val1, Val2, Val3 : out INTEGER) is
begin
Val1 := Data1.Value1;
Val2 := Data1.Value2;
Val3 := Data1.Value3;
end Decompose;
procedure Assign_Struct(Data1 : out DATA_STRUCTURE;
Data2 : in DATA_STRUCTURE) is
begin
Data1 := Data2;
end Assign_Struct;
function Compare(Data1, Data2 : DATA_STRUCTURE) return BOOLEAN is
begin
return Data1 = Data2;
end Compare;
end Three;
-- This program exercises the package Three as an illustration.
with Ada.Text_IO; use Ada.Text_IO;
with Three; use Three;
procedure LimPriv is
My_Data, Extra_Data : DATA_STRUCTURE;
begin
Assign_Struct(My_Data, Build_Structure(3, 7, 13));
Assign_Struct(Extra_Data, Build_Structure(-4, 77, 0));
Assign_Struct(My_Data, My_Data + Extra_Data);
if not Compare(My_Data, Extra_Data) then
Put_Line("The two structures are not equal.");
end if;
Assign_Struct(My_Data, Extra_Data);
if Compare(My_Data, Extra_Data) then
Put_Line("The two structures are equal now.");
end if;
-- The following line is illegal with the limited private type
-- My_Data.Value1 := My_Data.Value1 + 13;
end LimPriv;
-- Result of execution
-- The two structures are not equal.
-- The two structures are equal now.
-- Chapter 21 - Program 5
-- Note: This program is based on the example program from chapter
-- 18 named operover.ada, and is used to illustrate the different
-- style required when using a limited type.
package Shape is
type BOX is limited
record
Length : INTEGER;
Width : INTEGER;
Height : INTEGER;
end record;
-- function Make_A_Box(In_Length, In_Width, In_Height : INTEGER)
-- return BOX;
procedure Make_A_Box(Result_Box : out BOX;
In_Length, In_Width, In_Height : INTEGER);
function "="(Left, Right : BOX) return BOOLEAN;
function "+"(Left, Right : BOX) return BOX;
procedure Print_Box(Input_Box : BOX);
end Shape;
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
package body Shape is
procedure Make_A_Box(Result_Box : out BOX;
In_Length, In_Width, In_Height : INTEGER) is
begin
Result_Box.Length := In_Length;
Result_Box.Width := In_Width;
Result_Box.Height := In_Height;
end Make_A_Box;
function "="(Left, Right : BOX) return BOOLEAN is
begin
if (Left.Length = Right.Length and
Left.Width = Right.Width and
Left.Height = Right.Height) then
return TRUE;
else
return FALSE;
end if;
end "=";
function "+"(Left, Right : BOX) return BOX is
Temp_Box : BOX;
begin
Temp_Box.Length := Left.Length + Right.Length;
Temp_Box.Width := Left.Width + Right.Width;
Temp_Box.Height := Left.Height + Right.Height;
return Temp_Box;
end "+";
procedure Print_Box(Input_Box : BOX) is
begin
Put("Length = ");
Put(Input_Box.Length, 3);
Put(" Width = ");
Put(Input_Box.Width, 3);
Put(" Height = ");
Put(Input_Box.Height, 3);
New_Line;
end Print_Box;
end Shape;
with Ada.Text_IO, Shape;
use Ada.Text_IO;
use type Shape.BOX;
procedure OperOver is
Small_Box : Shape.BOX;
Big_Box : Shape.BOX;
begin
-- Small_Box := Shape.Make_A_Box(2, 3, 2);
-- Big_Box := Shape.Make_A_Box(4, 5, 3);
Shape.Make_A_Box(Small_Box, 2, 3, 2);
Shape.Make_A_Box(Big_Box, 4, 5, 3);
Shape.Print_Box(Small_Box);
Shape.Print_Box(Big_Box);
if Small_Box = Small_Box then
Put_Line("The small box is the same size as itself.");
else
Put_Line("The small box is not itself.");
end if;
if Small_Box /= Big_Box then
Put_Line("The boxes are different sizes.");
else
Put_Line("The boxes are the same size.");
end if;
end OperOver;
-- Result of execution
-- Length = 2 Width = 3 Height = 2
-- Length = 4 Width = 5 Height = 3
-- The small box is the same size as itself.
-- The boxes are different sizes.
-- Chapter 21 - Programming exercise 1
-- This package uses a data structure composed of three INTEGER
-- variables. It allow the user to add two structures, component
-- by component, or subtract component by component. Provision is
-- also made to build a structure from three numbers, or decompose
-- a structure into its components.
package Three is
type DATA_STRUCTURE is private;
function "+"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE;
function "-"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE;
function Build_Structure(Val1, Val2, Val3 : INTEGER) return
DATA_STRUCTURE;
procedure Decompose(Data1 : DATA_STRUCTURE;
Val1, Val2, Val3 : out INTEGER);
function Compare_Sum(Data1, Data2 : DATA_STRUCTURE) return BOOLEAN;
private
type DATA_STRUCTURE is
record
Value1 : INTEGER;
Value2 : INTEGER;
Value3 : INTEGER;
end record;
end Three;
package body Three is
function "+"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE is
Temp : DATA_STRUCTURE;
begin
Temp.Value1 := Data1.Value1 + Data2.Value1;
Temp.Value2 := Data1.Value2 + Data2.Value2;
Temp.Value3 := Data1.Value3 + Data2.Value3;
return Temp;
end "+";
function "-"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE is
Temp : DATA_STRUCTURE;
begin
Temp.Value1 := Data1.Value1 - Data2.Value1;
Temp.Value2 := Data1.Value2 - Data2.Value2;
Temp.Value3 := Data1.Value3 - Data2.Value3;
return Temp;
end "-";
function Build_Structure(Val1, Val2, Val3 : INTEGER) return
DATA_STRUCTURE is
Temp : DATA_STRUCTURE;
begin
Temp.Value1 := Val1;
Temp.Value2 := Val2;
Temp.Value3 := Val3;
return Temp;
end Build_Structure;
procedure Decompose(Data1 : DATA_STRUCTURE;
Val1, Val2, Val3 : out INTEGER) is
begin
Val1 := Data1.Value1;
Val2 := Data1.Value2;
Val3 := Data1.Value3;
end Decompose;
function Compare_Sum(Data1, Data2 : DATA_STRUCTURE)
return BOOLEAN is
begin
return (Data1.Value1 + Data1.Value2 + Data1.Value3) =
(Data2.Value1 + Data2.Value2 + Data2.Value3);
end Compare_Sum;
end Three;
-- This program exercises the package Three as an illustration.
with Ada.Text_IO; use Ada.Text_IO;
with Three; use Three;
procedure CH21_1 is
My_Data, Extra_Data : DATA_STRUCTURE;
Temp : DATA_STRUCTURE;
begin
My_Data := Build_Structure(3,7,13);
Extra_Data := Build_Structure(-4,77,0);
My_Data := My_Data + Extra_Data;
if My_Data /= Extra_Data then
Put_Line("The two structures are not equal.");
end if;
My_Data := Extra_Data;
if My_Data = Extra_Data then
Put_Line("The two structures are equal now.");
end if;
-- The following line is illegal with the private type.
-- My_Data.Value1 := My_Data.Value1 + 13;
Temp := Build_Structure(13,0,0);
My_Data := My_Data + Temp;
if not Compare_Sum(My_Data, Temp) then
Put_Line("My_Data and Temp are not equal.");
end if;
My_Data := Build_Structure(1,9,3);
if Compare_Sum(My_Data, Temp) then
Put_Line("My_Data and Temp are equal.");
end if;
end CH21_1;
-- Result of execution
-- The two structures are not equal.
-- The two structures are equal now.
-- My_Data and Temp are not equal.
-- My_Data and Temp are equal.
-- Chapter 21 - Programming exercise 2
-- This package uses a data structure composed of three INTEGER
-- variables. It allow the user to add two structures, component
-- by component, or subtract component by component. Provision is
-- also made to build a structure from three numbers, or decompose
-- a structure into its components.
package Three is
type DATA_STRUCTURE is private;
function "+"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE;
function "-"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE;
function Build_Structure(Val1, Val2, Val3 : INTEGER) return
DATA_STRUCTURE;
procedure Decompose(Data1 : DATA_STRUCTURE;
Val1, Val2, Val3 : out INTEGER);
function Compare_Sum(Data1, Data2 : DATA_STRUCTURE) return BOOLEAN;
private
type DATA_STRUCTURE is array(1..3) of INTEGER;
end Three;
package body Three is
function "+"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE is
Temp : DATA_STRUCTURE;
begin
Temp(1) := Data1(1) + Data2(1);
Temp(2) := Data1(2) + Data2(2);
Temp(3) := Data1(3) + Data2(3);
return Temp;
end "+";
function "-"(Data1, Data2 : DATA_STRUCTURE) return DATA_STRUCTURE is
Temp : DATA_STRUCTURE;
begin
Temp(1) := Data1(1) - Data2(1);
Temp(2) := Data1(2) - Data2(2);
Temp(3) := Data1(3) - Data2(3);
return Temp;
end "-";
function Build_Structure(Val1, Val2, Val3 : INTEGER) return
DATA_STRUCTURE is
Temp : DATA_STRUCTURE;
begin
Temp(1) := Val1;
Temp(2) := Val2;
Temp(3) := Val3;
return Temp;
end Build_Structure;
procedure Decompose(Data1 : DATA_STRUCTURE;
Val1, Val2, Val3 : out INTEGER) is
begin
Val1 := Data1(1);
Val2 := Data1(2);
Val3 := Data1(3);
end Decompose;
function Compare_Sum(Data1, Data2 : DATA_STRUCTURE)
return BOOLEAN is
begin
return (Data1(1) + Data1(2) + Data1(3)) =
(Data2(1) + Data2(2) + Data2(3));
end Compare_Sum;
end Three;
-- This program exercises the package Three as an illustration.
with Ada.Text_IO; use Ada.Text_IO;
with Three; use Three;
procedure CH21_2 is
My_Data, Extra_Data : DATA_STRUCTURE;
Temp : DATA_STRUCTURE;
begin
My_Data := Build_Structure(3,7,13);
Extra_Data := Build_Structure(-4,77,0);
My_Data := My_Data + Extra_Data;
if My_Data /= Extra_Data then
Put_Line("The two structures are not equal.");
end if;
My_Data := Extra_Data;
if My_Data = Extra_Data then
Put_Line("The two structures are equal now.");
end if;
-- The following line is illegal with the private type.
-- My_Data.Value1 := My_Data.Value1 + 13;
Temp := Build_Structure(13,0,0);
My_Data := My_Data + Temp;
if not Compare_Sum(My_Data, Temp) then
Put_Line("My_Data and Temp are not equal.");
end if;
My_Data := Build_Structure(1,9,3);
if Compare_Sum(My_Data, Temp) then
Put_Line("My_Data and Temp are equal.");
end if;
end CH21_2;
-- Result of execution
-- The two structures are not equal.
-- The two structures are equal now.
-- My_Data and Temp are not equal.
-- My_Data and Temp are equal.
-- Chapter 21 - Programming exercise 3
package Stuff is
type MONTH_NAMES is (JAN, FEB, MAR, APR, MAY, JUN,
JUL, AUG, SEP, OCT, NOV, DEC);
type DATE is
record
Month : MONTH_NAMES;
Day : INTEGER range 1..31;
Year : INTEGER range 1800..2100;
end record;
Pi : constant := 3.14159;
Two_Pi : constant := 2 * Pi;
end Stuff;
with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO, Stuff;
use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO, Stuff;
procedure CH21_3 is
package Enum_IO is new Ada.Text_IO.Enumeration_IO(MONTH_NAMES);
use Enum_IO;
Birth_Day : DATE := (OCT, 18, 1938);
begin
Put("My birthday is ");
Put(Birth_Day.Month);
Put(Birth_Day.Day, 3);
Put(",");
Put(Birth_Day.Year, 5);
New_Line(2);
Put("The value of Pi is");
Put(Pi, 3, 6, 0);
New_Line;
end CH21_3;
-- Result of execution
-- My birthday is OCT 18, 1938
--
-- The value of Pi is 3.141590
-- Chapter 22 - Program 1
package Starter is
type MY_INTEGER is new INTEGER range 5..150;
type MY_FLOAT is new FLOAT;
type WOODEN_BOX is
record
Length : FLOAT;
Width : FLOAT;
Height : FLOAT;
end record;
function "+" (Left, Right : WOODEN_BOX) return WOODEN_BOX;
type STEEL_BOX is new WOODEN_BOX;
function "-" (Left, Right : STEEL_BOX) return STEEL_BOX;
end Starter;
-- There is no implementation for this example program.
-- Result of execution
--
-- (This fragment cannot be executed)
-- Chapter 22 - Program 2
package Conveyance1 is
-- This is a very simple transportation type.
type TRANSPORT is
record
Wheels : INTEGER;
Weight : FLOAT;
end record;
procedure Set_Values(Vehicle_In : in out TRANSPORT;
Wheels_In : INTEGER;
Weight_In : FLOAT);
function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER;
function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT;
-- This CAR type extends the functionality of the TRANSPORT type.
type CAR is new TRANSPORT;
function Tire_Loading(Vehicle_In : CAR) return FLOAT;
end Conveyance1;
package body Conveyance1 is
-- Subprograms for the TRANSPORT record type.
procedure Set_Values(Vehicle_In : in out TRANSPORT;
Wheels_In : INTEGER;
Weight_In : FLOAT) is
begin
Vehicle_In.Wheels := Wheels_In;
Vehicle_In.Weight := Weight_In;
end Set_Values;
function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER is
begin
return Vehicle_In.Wheels;
end Get_Wheels;
function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT is
begin
return Vehicle_In.Weight;
end Get_Weight;
-- Subprogram for the CAR record type.
function Tire_Loading(Vehicle_In : CAR) return FLOAT is
begin
return Vehicle_In.Weight / FLOAT(Vehicle_In.Wheels);
end Tire_Loading;
end Conveyance1;
-- Results of execution
--
-- (This package cannot be executed alone.)
-- Chapter 22 - Program 3
with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO, Conveyance1;
use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO, Conveyance1;
procedure Vehicle1 is
Hummer : TRANSPORT;
Ford : CAR;
begin

Set_Values(Hummer, 4, 5760.0);
Put("The Hummer has");
Put(Get_Wheels(Hummer), 2);
Put(" wheels.");
New_Line;

Put("The Hummer has");
Put(Hummer.Wheels, 2);
Put(" wheels.");
New_Line;
Set_Values(Ford, 4, 3204.0);

Put("The Ford has");
Put(Get_Wheels(Ford), 2);
Put(" wheels. The tire load is ");
Put(Tire_Loading(Ford), 3, 1, 0);
Put(" pounds per tire.");
New_Line;

end Vehicle1;
-- Result of execution
--
-- The Hummer has 4 wheels.
-- The Hummer has 4 wheels.
-- The Ford has 4 wheels. The tire load is 801.0 pounds per tire.
-- Chapter 22 - Program 4
package Conveyance2 is
type TRANSPORT is private;
procedure Set_Values(Vehicle_In : in out TRANSPORT;
Wheels_In : INTEGER;
Weight_In : FLOAT);
function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER;
function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT;
type CAR is private;
procedure Set_Values(Vehicle_In : in out CAR;
Wheels_In : INTEGER;
Weight_In : FLOAT);
function Get_Wheels(Vehicle_In : CAR) return INTEGER;
function Tire_Loading(Vehicle_In : CAR) return FLOAT;
private -- private part of the specification
type TRANSPORT is
record
Wheels : INTEGER;
Weight : FLOAT;
end record;
type CAR is new TRANSPORT;
end Conveyance2;
package body Conveyance2 is
-- Subprograms for the TRANSPORT record type.
procedure Set_Values(Vehicle_In : in out TRANSPORT;
Wheels_In : INTEGER;
Weight_In : FLOAT) is
begin
Vehicle_In.Wheels := Wheels_In;
Vehicle_In.Weight := Weight_In;
end Set_Values;
function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER is
begin
return Vehicle_In.Wheels;
end Get_Wheels;
function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT is
begin
return Vehicle_In.Weight;
end Get_Weight;
-- Subprogram for the CAR record type.
procedure Set_Values(Vehicle_In : in out CAR;
Wheels_In : INTEGER;
Weight_In : FLOAT) is
begin
Vehicle_In.Wheels := Wheels_In;
Vehicle_In.Weight := Weight_In;
end Set_Values;
function Get_Wheels(Vehicle_In : CAR) return INTEGER is
begin
return Vehicle_In.Wheels;
end Get_Wheels;
function Tire_Loading(Vehicle_In : CAR) return FLOAT is
begin
return Vehicle_In.Weight / FLOAT(Vehicle_In.Wheels);
end Tire_Loading;
end Conveyance2;
-- Chapter 22 - Program 5
with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO, Conveyance2;
use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO, Conveyance2;
procedure Vehicle2 is
Hummer : TRANSPORT;
Ford : CAR;
begin

Set_Values(Hummer, 4, 5760.0);
Put("The Hummer has");
Put(Get_Wheels(Hummer), 2);
Put(" wheels.");
New_Line;
Set_Values(Ford, 4, 3204.0);

Put("The Ford has");
Put(Get_Wheels(Ford), 2);
Put(" wheels. The tire load is ");
Put(Tire_Loading(Ford), 3, 1, 0);
Put(" pounds per tire.");
New_Line;

end Vehicle2;
-- Result of execution
--
-- The Hummer has 4 wheels.
-- The Ford has 4 wheels. The tire load is 801.0 pounds per tire.
-- Chapter 22 - Program 6
package Conveyance3 is
-- This is a very simple transportaion type.
type TRANSPORT is tagged private;
procedure Set_Values(Vehicle_In : in out TRANSPORT;
Wheels_In : INTEGER;
Weight_In : FLOAT);
function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER;
function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT;
type CAR is new TRANSPORT with private;
procedure Set_Values(Vehicle_In : in out CAR;
Passenger_Count_In : INTEGER);
function Get_Passenger_Count(Vehicle_In : CAR) return INTEGER;
type TRUCK is new TRANSPORT with private;
procedure Set_Values(Vehicle_In : in out TRUCK;
Wheels_In : INTEGER;
Weight_In : FLOAT;
Passenger_Count_In : INTEGER;
Payload_In : FLOAT);
function Get_Passenger_Count(Vehicle_In : TRUCK) return INTEGER;
type BICYCLE is new TRANSPORT with private;
private
type TRANSPORT is tagged
record
Wheels : INTEGER;
Weight : FLOAT;
end record;
type CAR is new TRANSPORT with
record
Passenger_Count : INTEGER;
end record;
type TRUCK is new TRANSPORT with
record
Passenger_Count : INTEGER;
Payload : FLOAT;
end record;
type BICYCLE is new TRANSPORT with null record;
end Conveyance3;
package body Conveyance3 is
-- Subprograms for the TRANSPORT3 record
procedure Set_Values(Vehicle_In : in out TRANSPORT;
Wheels_In : INTEGER;
Weight_In : FLOAT) is
begin
Vehicle_In.Wheels := Wheels_In;
Vehicle_In.Weight := Weight_In;
end Set_Values;
function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER is
begin
return Vehicle_In.Wheels;
end Get_Wheels;
function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT is
begin
return Vehicle_In.Weight;
end Get_Weight;
-- Subprograms for the CAR record
procedure Set_Values(Vehicle_In : in out CAR;
Passenger_Count_In : INTEGER) is
begin
Vehicle_In.Passenger_Count := Passenger_Count_In;
end Set_Values;
function Get_Passenger_Count(Vehicle_In : CAR) return INTEGER is
begin
return Vehicle_In.Passenger_Count;
end Get_Passenger_Count;
-- Subprograms for the TRUCK record
procedure Set_Values(Vehicle_In : in out TRUCK;
Wheels_In : INTEGER;
Weight_In : FLOAT;
Passenger_Count_In : INTEGER;
Payload_In : FLOAT) is
begin
-- This is one way to set the values in the base class
Vehicle_In.Wheels := Wheels_In;
Vehicle_In.Weight := Weight_In;
-- This is another way to set the values in the base class
Set_Values(TRANSPORT(Vehicle_In), Wheels_In, Weight_In);
-- This sets the values in this class
Vehicle_In.Passenger_Count := Passenger_Count_In;
Vehicle_In.Payload := Payload_In;
end Set_Values;
function Get_Passenger_Count(Vehicle_In : TRUCK) return INTEGER is
begin
return Vehicle_In.Passenger_Count;
end Get_Passenger_Count;
end Conveyance3;
-- Result of execution
--
-- (This program cannot be executed alone.)
-- Chapter 22 - Program 7
with Ada.Text_IO, Ada.Integer_Text_IO, Conveyance3;
use Ada.Text_IO, Ada.Integer_Text_IO, Conveyance3;
procedure Vehicle3 is
Hummer : TRANSPORT;
Limo : CAR;
Chevy : CAR;
Dodge : TRUCK;
Ford : TRUCK;
begin

Set_Values(Hummer, 4, 5760.0);
Set_Values(Limo, 8);
Set_Values(TRANSPORT(Limo), 4, 3750.0);
Set_Values(Chevy, 5);
Set_Values(TRANSPORT(Chevy), 4, 2560.0);
Set_Values(Dodge, 6, 4200.0, 3, 1800.0);
Set_Values(Ford, 4, 2800.0, 3, 1100.0);
Put("The Ford truck has");
Put(Get_Wheels(Ford), 2);
Put(" wheels, and can carry");
Put(Get_Passenger_Count(Ford), 2);
Put(" passengers.");
New_Line;

end Vehicle3;
-- Result of execution
--
-- The Ford truck has 4 wheels, and can carry 3 passengers.
-- Chapter 22 - Program 8
with Ada.Text_IO, Ada.Finalization;
use Ada.Text_IO, Ada.Finalization;
package Component is
type WIDGET is new CONTROLLED with
record
Size : INTEGER;
Number : INTEGER;
end record;
procedure Initialize(Item : in out WIDGET);
procedure Adjust(Item : in out WIDGET);
procedure Finalize(Item : in out WIDGET);
end Component;
package body Component is
procedure Initialize(Item : in out WIDGET) is
begin
Put_Line(" This is from Initialize");
end Initialize;
procedure Adjust(Item : in out WIDGET) is
begin
Put_Line(" This is from Adjust");
end Adjust;
procedure Finalize(Item : in out WIDGET) is
begin
Put_Line(" This is from Finalize");
end Finalize;
end Component;
with Ada.Text_IO, Component;
use Ada.Text_IO, Component;
procedure Control is
Paper : WIDGET;
Pencil : WIDGET;
begin
Put_Line("Beginning this simple program");
Paper.Size := 11;
Paper.Number := 25;
Put_Line("Paper record filled with data");
Pencil := Paper;
Put_Line("Paper copied to Pencil");
Paper := Pencil;
Put_Line("Pencil copied to Paper");
end Control;
-- Result of execution
--
-- This is from Initialize
-- This is from Initialize
-- Beginning this simple program
-- Paper record filled with data
-- This is from Finalize
-- This is from Adjust
-- Paper copied to Pencil
-- This is from Finalize
-- This is from Adjust
-- Pencil copied to Paper
-- This is from Finalize
-- This is from Finalize
-- Chapter 22 - Exercise 1
with Ada.Text_IO, Ada.Integer_Text_IO, Conveyance3;
use Ada.Text_IO, Ada.Integer_Text_IO, Conveyance3;
procedure CH22_1 is
Hummer : TRANSPORT;
Limo : CAR;
Chevy : CAR;
Dodge : TRUCK;
Ford : TRUCK;
Schwinn : BICYCLE;
begin

Set_Values(Hummer, 4, 5760.0);
Set_Values(Limo, 8);
Set_Values(TRANSPORT(Limo), 4, 3750.0);
Set_Values(Chevy, 5);
Set_Values(TRANSPORT(Chevy), 4, 2560.0);
Set_Values(Dodge, 6, 4200.0, 3, 1800.0);
Set_Values(Ford, 4, 2800.0, 3, 1100.0);
Set_Values(Schwinn, 1, 75.0);
Put("The Ford truck has");
Put(Get_Wheels(Ford), 2);
Put(" wheels, and can carry");
Put(Get_Passenger_Count(Ford), 2);
Put(" passengers.");
New_Line;

Put("The Schwinn has");
Put(Get_Wheels(Schwinn), 2);
Put(" wheel.");
New_Line;
end CH22_1;
-- Result of execution
--
-- The Ford truck has 4 wheels, and can carry 3 passengers.
-- The Schwinn has one wheel.
-- Chapter 22 - Exercise 2
package CH22_2 is
-- This is a very simple transportaion type.
type TRANSPORT is tagged private;
procedure Set_Values(Vehicle_In : in out TRANSPORT;
Wheels_In : INTEGER;
Weight_In : FLOAT);
function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER;
function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT;
type CAR is new TRANSPORT with private;
procedure Set_Values(Vehicle_In : in out CAR;
Passenger_Count_In : INTEGER);
function Get_Passenger_Count(Vehicle_In : CAR) return INTEGER;
type TRUCK is new TRANSPORT with private;
procedure Set_Values(Vehicle_In : in out TRUCK;
Wheels_In : INTEGER;
Weight_In : FLOAT;
Passenger_Count_In : INTEGER;
Payload_In : FLOAT);
function Get_Passenger_Count(Vehicle_In : TRUCK) return INTEGER;
type BICYCLE is new TRANSPORT with private;
function Get_Weight_In_Ounces(Vehicle_In : BICYCLE) return FLOAT;
private
type TRANSPORT is tagged
record
Wheels : INTEGER;
Weight : FLOAT;
end record;
type CAR is new TRANSPORT with
record
Passenger_Count : INTEGER;
end record;
type TRUCK is new TRANSPORT with
record
Passenger_Count : INTEGER;
Payload : FLOAT;
end record;
type BICYCLE is new TRANSPORT with null record;
end CH22_2;
package body CH22_2 is
-- Subprograms for the TRANSPORT3 record
procedure Set_Values(Vehicle_In : in out TRANSPORT;
Wheels_In : INTEGER;
Weight_In : FLOAT) is
begin
Vehicle_In.Wheels := Wheels_In;
Vehicle_In.Weight := Weight_In;
end Set_Values;
function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER is
begin
return Vehicle_In.Wheels;
end Get_Wheels;
function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT is
begin
return Vehicle_In.Weight;
end Get_Weight;
-- Subprograms for the CAR record
procedure Set_Values(Vehicle_In : in out CAR;
Passenger_Count_In : INTEGER) is
begin
Vehicle_In.Passenger_Count := Passenger_Count_In;
end Set_Values;
function Get_Passenger_Count(Vehicle_In : CAR) return INTEGER is
begin
return Vehicle_In.Passenger_Count;
end Get_Passenger_Count;
-- Subprograms for the TRUCK record
procedure Set_Values(Vehicle_In : in out TRUCK;
Wheels_In : INTEGER;
Weight_In : FLOAT;
Passenger_Count_In : INTEGER;
Payload_In : FLOAT) is
begin
-- This is one way to set the values in the base class
Vehicle_In.Wheels := Wheels_In;
Vehicle_In.Weight := Weight_In;
-- This is another way to set the values in the base class
Set_Values(TRANSPORT(Vehicle_In), Wheels_In, Weight_In);
-- This sets the values in this class
Vehicle_In.Passenger_Count := Passenger_Count_In;
Vehicle_In.Payload := Payload_In;
end Set_Values;
function Get_Passenger_Count(Vehicle_In : TRUCK) return INTEGER is
begin
return Vehicle_In.Passenger_Count;
end Get_Passenger_Count;
-- Subprograms for the TRUCK record
function Get_Weight_In_Ounces(Vehicle_In : BICYCLE) return FLOAT is
begin
return 16.0 * Vehicle_In.Weight;
end Get_Weight_In_Ounces;
end CH22_2;
-- Result of execution
--
-- (This program cannot be executed alone.)
-- Chapter 23 - Program 1
with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO;
package Conveyance4 is
-- A very simple transportation record.
type TRANSPORT is tagged private;
procedure Set_Values(Vehicle_In : in out TRANSPORT;
Wheels_In : INTEGER;
Weight_In : FLOAT);
function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER;
function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT;
-- Extend TRANSPORT to a CAR type.
type CAR is new TRANSPORT with private;
procedure Set_Values(Vehicle_In : in out CAR;
Passenger_Count_In : INTEGER);
function Get_Passenger_Count(Vehicle_In : CAR) return INTEGER;
-- Extend TRANSPORT to a TRUCK type.
type TRUCK is new TRANSPORT with private;
procedure Set_Values(Vehicle_In : in out TRUCK;
Wheels_In : INTEGER;
Weight_In : FLOAT;
Passenger_Count_In : INTEGER;
Payload_In : FLOAT);
function Get_Passenger_Count(Vehicle_In : TRUCK) return INTEGER;
-- Derive an identical type for BICYCLE.
type BICYCLE is new TRANSPORT with private;
-- Print_Values is a class-wide operation. It can accept objects
-- of any type within the TRANSPORT heirarchy.
procedure Print_Values(Any_Vehicle : TRANSPORT'Class);
private
type TRANSPORT is tagged
record
Wheels : INTEGER;
Weight : FLOAT;
end record;
type CAR is new TRANSPORT with
record
Passenger_Count : INTEGER;
end record;
type TRUCK is new TRANSPORT with
record
Passenger_Count : INTEGER;
Payload : FLOAT;
end record;
type BICYCLE is new TRANSPORT with null record;
end Conveyance4;
package body Conveyance4 is
-- Subprograms for the TRANSPORT record
procedure Set_Values(Vehicle_In : in out TRANSPORT;
Wheels_In : INTEGER;
Weight_In : FLOAT) is
begin
Vehicle_In.Wheels := Wheels_In;
Vehicle_In.Weight := Weight_In;
end Set_Values;
function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER is
begin
return Vehicle_In.Wheels;
end Get_Wheels;
function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT is
begin
return Vehicle_In.Weight;
end Get_Weight;
-- Subprograms for the CAR record
procedure Set_Values(Vehicle_In : in out CAR;
Passenger_Count_In : INTEGER) is
begin
Vehicle_In.Passenger_Count := Passenger_Count_In;
end Set_Values;
function Get_Passenger_Count(Vehicle_In : CAR) return INTEGER is
begin
return Vehicle_In.Passenger_Count;
end Get_Passenger_Count;
-- Subprograms for the TRUCK record
procedure Set_Values(Vehicle_In : in out TRUCK;
Wheels_In : INTEGER;
Weight_In : FLOAT;
Passenger_Count_In : INTEGER;
Payload_In : FLOAT) is
begin
-- This is one way to set the values in the base class
Vehicle_In.Wheels := Wheels_In;
Vehicle_In.Weight := Weight_In;
-- This is another way to set the values in the base class
Set_Values(TRANSPORT(Vehicle_In), Wheels_In, Weight_In);
-- This sets the values in this class
Vehicle_In.Passenger_Count := Passenger_Count_In;
Vehicle_In.Payload := Payload_In;
end Set_Values;
function Get_Passenger_Count(Vehicle_In : TRUCK) return INTEGER is
begin
return Vehicle_In.Passenger_Count;
end Get_Passenger_Count;
-- Print_Values is a class-wide operation. It can accept objects
-- of any type within the TRANSPORTheirarchy.
procedure Print_Values(Any_Vehicle : TRANSPORT'Class) is
begin
Put("This vehicle has");
Put(Any_Vehicle.Wheels, 2);
Put(" wheels, and weighs");
Put(Any_Vehicle.Weight, 5, 1, 0);
Put(" pounds.");
New_Line;
-- The following line of code will produce an error because TRANSPORT
-- and BICYCLE do not contain this variable.
-- Put(Any_Vehicle.Passenger_Count, 5);
end Print_Values;
end Conveyance4;
-- Result of execution
--
-- (This program cannot be executed alone.)
-- Chapter 23 - Program 2
with Ada.Text_IO, Ada.Integer_Text_IO, Conveyance4;
use Ada.Text_IO, Ada.Integer_Text_IO, Conveyance4;
procedure Vehicle4 is
Hummer : TRANSPORT;
Limo : CAR;
Chevy : CAR;
Dodge : TRUCK;
Ford : TRUCK;
begin

Set_Values(Hummer, 4, 5760.0);
Set_Values(Limo, 8);
Set_Values(TRANSPORT(Limo), 4, 3750.0);
Set_Values(Chevy, 5);
Set_Values(TRANSPORT(Chevy), 4, 2560.0);
Set_Values(Dodge, 6, 4200.0, 3, 1800.0);
Set_Values(Ford, 4, 2800.0, 3, 1100.0);
-- Print out the data for the Ford truck just to pick on one
-- of the objects for demonstration purposes.
Put("The Ford truck has");
Put(Get_Wheels(Ford), 2);
Put(" wheels, and can carry");
Put(Get_Passenger_Count(Ford), 2);
Put(" passengers.");
New_Line;

-- Now, let's call the class-wide procedure 5 times.
Print_Values(Hummer);
Print_Values(Limo);
Print_Values(Chevy);
Print_Values(Dodge);
Print_Values(Ford);
end Vehicle4;
-- Result of execution
--
-- The Ford truck has 4 wheels and can carry 3 passengers.
-- This vehicle has 4 wheels, and weighs 5760.0 pounds.
-- This vehicle has 4 wheels, and weighs 3750.0 pounds.
-- This vehicle has 4 wheels, and weighs 2560.0 pounds.
-- This vehicle has 6 wheels, and weighs 4200.0 pounds.
-- This vehicle has 4 wheels, and weighs 2800.0 pounds.
-- Chapter 23 - Program 3
with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO;
package Conveyance5 is
-- Begin with a basic transportation type.
type TRANSPORT is tagged private;
procedure Set_Values(Vehicle_In : in out TRANSPORT;
Wheels_In : INTEGER);
function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER;
procedure Describe(Vehicle_In : TRANSPORT);
-- Extend the basic type to a CAR type.
type CAR is new TRANSPORT with private;
procedure Set_Values(Vehicle_In : in out CAR;
Passenger_Count_In : INTEGER);
function Get_Passenger_Count(Vehicle_In : CAR) return INTEGER;
procedure Describe(Vehicle_In : CAR);
-- Extend the basic type to a TRUCK type.
type TRUCK is new TRANSPORT with private;
procedure Set_Values(Vehicle_In : in out TRUCK;
Wheels_In : INTEGER;
Passenger_Count_In : INTEGER);
function Get_Passenger_Count(Vehicle_In : TRUCK) return INTEGER;
procedure Describe(Vehicle_In : TRUCK);
-- Extend the basic type to the BICYCLE type.
type BICYCLE is new TRANSPORT with private;
procedure Describe(Vehicle_In : BICYCLE);
-- Print_Values is a class-wide operation. It can accept objects
-- of any type within the TRANSPORT heirarchy.
procedure Print_Values(Any_Vehicle : TRANSPORT'Class);
private
type TRANSPORT is tagged
record
Wheels : INTEGER;
end record;
type CAR is new TRANSPORT with
record
Passenger_Count : INTEGER;
end record;
type TRUCK is new TRANSPORT with
record
Passenger_Count : INTEGER;
end record;
type BICYCLE is new TRANSPORT with null record;
end Conveyance5;
package body Conveyance5 is
-- Subprograms for the TRANSPORT record
procedure Set_Values(Vehicle_In : in out TRANSPORT;
Wheels_In : INTEGER) is
begin
Vehicle_In.Wheels := Wheels_In;
end Set_Values;
function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER is
begin
return Vehicle_In.Wheels;
end Get_Wheels;
procedure Describe(Vehicle_In : TRANSPORT) is
begin
Put("We are in the TRANSPORT procedure.");
new_Line;
end Describe;
-- Subprograms for the CAR record
procedure Set_Values(Vehicle_In : in out CAR;
Passenger_Count_In : INTEGER) is
begin
Vehicle_In.Passenger_Count := Passenger_Count_In;
end Set_Values;
function Get_Passenger_Count(Vehicle_In : CAR) return INTEGER is
begin
return Vehicle_In.Passenger_Count;
end Get_Passenger_Count;
procedure Describe(Vehicle_In : CAR) is
begin
Put("We are in the CAR procedure.");
new_Line;
end Describe;
-- Subprograms for the TRUCK record
procedure Set_Values(Vehicle_In : in out TRUCK;
Wheels_In : INTEGER;
Passenger_Count_In : INTEGER) is
begin
-- This is one way to set the values in the base class
Vehicle_In.Wheels := Wheels_In;
-- This is another way to set the values in the base class
Set_Values(TRANSPORT(Vehicle_In), Wheels_In);
-- This sets the values in this class
Vehicle_In.Passenger_Count := Passenger_Count_In;
end Set_Values;
function Get_Passenger_Count(Vehicle_In : TRUCK) return INTEGER is
begin
return Vehicle_In.Passenger_Count;
end Get_Passenger_Count;
procedure Describe(Vehicle_In : TRUCK) is
begin
Put("We are in the TRUCK procedure.");
new_Line;
end Describe;
-- Subprograms for the BICYCLE record
procedure Describe(Vehicle_In : BICYCLE) is
begin
Put("We are in the BICYCLE procedure.");
New_Line;
end Describe;
-- Print_Values is a class-wide operation. It can accept objects
-- of any type within the TRANSPORT heirarchy.
procedure Print_Values(Any_Vehicle : TRANSPORT'Class) is
begin
-- Describe(Any_Vehicle);
Put("This vehicle has");
Put(Any_Vehicle.Wheels, 2);
Put(" wheels.");
New_Line;
-- The following line of code will produce an error because TRANSPORT
-- and BICYCLE do not contain this variable.
-- Put(Any_Vehicle.Passenger_Count, 5);
end Print_Values;
end Conveyance5;
-- Result of execution
--
-- (This program cannot be executed alone.)
-- Chapter 23 - Program 4
with Ada.Text_IO, Ada.Integer_Text_IO, Conveyance5;
use Ada.Text_IO, Ada.Integer_Text_IO, Conveyance5;
procedure Vehicle5 is
Hummer : aliased TRANSPORT;
Limo : aliased CAR;
Chevy : aliased CAR;
Dodge : aliased TRUCK;
Ford : aliased TRUCK;
Schwinn : aliased BICYCLE;
type TRANSPORT_ACCESS is access all TRANSPORT'Class;
Any_Pt : TRANSPORT_ACCESS;
begin
Set_Values(Hummer, 4);
Set_Values(Limo, 8);
Set_Values(TRANSPORT(Limo), 4);
Set_Values(Chevy, 5);
Set_Values(TRANSPORT(Chevy), 4);
Set_Values(Dodge, 6, 3);
Set_Values(Ford, 4, 3);
Set_Values(Schwinn, 2);
-- Print out the data for the Ford truck just to pick on one.
Put("The Ford truck has");
Put(Get_Wheels(Ford), 2);
Put(" wheels, and can carry");
Put(Get_Passenger_Count(Ford), 2);
Put(" passengers.");
New_Line;

-- Now, let's call the class-wide procedure 6 times.
Print_Values(Hummer);
Print_Values(Limo);
Print_Values(Chevy);
Print_Values(Dodge);
Print_Values(Ford);
Print_Values(Schwinn);
Any_Pt := Hummer'Access;
Describe(Any_Pt.all);
Any_Pt := Limo'Access;
Describe(Any_Pt.all);
Any_Pt := Chevy'Access;
Describe(Any_Pt.all);
Any_Pt := Dodge'Access;
Describe(Any_Pt.all);
Any_Pt := Ford'Access;
Describe(Any_Pt.all);
Any_Pt := Schwinn'Access;
Describe(Any_Pt.all);
end Vehicle5;
-- Result of execution
--
-- The Ford truck has 4 wheels and can carry 3 passengers.
-- This vehicle has 4 wheels.
-- This vehicle has 4 wheels.
-- This vehicle has 4 wheels.
-- This vehicle has 6 wheels.
-- This vehicle has 4 wheels.
-- This vehicle has 2 wheels.
-- We are in the TRANSPORT procedure.
-- We are in the CAR procedure.
-- We are in the CAR procedure.
-- We are in the TRUCK procedure.
-- We are in the TRUCK procedure.
-- We are in the BICYCLE procedure.
-- Chapter 23 - Program 5
with Ada.Text_IO;
use Ada.Text_IO;
package Person is
type EMPLOYEE is tagged private;
procedure Display(Person_In : EMPLOYEE);
private
type EMPLOYEE is tagged
record
Name : STRING(1..25);
Name_Length : INTEGER;
Salary : INTEGER;
end record;
end Person;
package body Person is
procedure Display(Person_In : EMPLOYEE) is
begin
Put("This message should never be displayed!");
end Display;
end Person;
-- Result of execution
--
-- (This package cannot be executed alone.)
-- Chapter 23 - Program 6
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
package Person.Positions is
-- The SUPERVISOR type has a title.
type SUPERVISOR is new EMPLOYEE with private;
procedure Init_Data(In_Person : in out SUPERVISOR;
In_Name : STRING;
In_Salary : INTEGER;
In_Title : STRING);
procedure Display(In_Person : SUPERVISOR);
-- The PROGRAMMER type has a language preference.
type PROGRAMMER is new EMPLOYEE with private;
procedure Init_Data(In_Person : in out PROGRAMMER;
In_Name : STRING;
In_Salary : INTEGER;
In_Title : STRING;
In_Language : STRING);
procedure Display(In_Person : PROGRAMMER);
-- The SECRETARY type has a typing speed.
type SECRETARY is new EMPLOYEE with private;
procedure Init_Data(In_Person : in out SECRETARY;
In_Name : STRING;
In_Salary : INTEGER;
In_ShortHand : BOOLEAN;
In_Speed : INTEGER);
procedure Display(In_Person : SECRETARY);
private
type SUPERVISOR is new EMPLOYEE with
record
Title : STRING(1..25);
Title_Length : INTEGER;
end record;
type PROGRAMMER is new EMPLOYEE with
record
Title : STRING(1..25);
Title_Length : INTEGER;
Language : STRING(1..25);
Language_Length : INTEGER;
end record;
type SECRETARY is new EMPLOYEE with
record
Shorthand : BOOLEAN;
Typing_Speed : INTEGER;
end record;

end Person.Positions;
package body Person.Positions is
-- Subprograms for the SUPERVISOR type.
procedure Init_Data(In_Person : in out SUPERVISOR;
In_Name : STRING;
In_Salary : INTEGER;
In_Title : STRING) is
begin
In_Person.Name_Length := In_Name'Length;
for Index in In_Name'Range loop
In_Person.Name(Index) := In_Name(Index);
end loop;
In_Person.Salary := In_Salary;
In_Person.Title_Length := In_Title'Length;
for Index in In_Title'Range loop
In_Person.Title(Index) := In_Title(Index);
end loop;
end Init_Data;
procedure Display(In_Person : SUPERVISOR) is
begin
for Index in 1..In_Person.Name_Length loop
Put(In_Person.Name(Index));
end loop;
Put(" is a supervisor, and is the ");
for Index in 1..In_Person.Title_Length loop
Put(In_Person.Title(Index));
end loop;
Put(" of the company");
New_Line;
end Display;
-- Subprograms for the PROGRAMMER type.
procedure Init_Data(In_Person : in out PROGRAMMER;
In_Name : STRING;
In_Salary : INTEGER;
In_Title : STRING;
In_Language : STRING) is
begin
In_Person.Name_Length := In_Name'Length;
for Index in In_Name'Range loop
In_Person.Name(Index) := In_Name(Index);
end loop;
In_Person.Salary := In_Salary;
In_Person.Title_Length := In_Title'Length;
for Index in In_Title'Range loop
In_Person.Title(Index) := In_Title(Index);
end loop;
In_Person.Language_Length := In_Language'Length;
for Index in In_Language'Range loop
In_Person.Language(Index) := In_Language(Index);
end loop;
end Init_Data;
procedure Display(In_Person : PROGRAMMER) is
begin
for Index in 1..In_Person.Name_Length loop
Put(In_Person.Name(Index));
end loop;
Put(" is a programmer specializing in ");
for Index in 1..In_Person.Language_Length loop
Put(In_Person.Language(Index));
end loop;
Put(". He makes ");
Put(In_Person.Salary, 6);
Put(" dollars per year.");
New_Line;
end Display;
-- Subprograms for the SECRETARY type.
procedure Init_Data(In_Person : in out SECRETARY;
In_Name : STRING;
In_Salary : INTEGER;
In_ShortHand : BOOLEAN;
In_Speed : INTEGER) is
begin
In_Person.Name_Length := In_Name'Length;
for Index in In_Name'Range loop
In_Person.Name(Index) := In_Name(Index);
end loop;
In_Person.Salary := In_Salary;
In_Person.Shorthand := In_Shorthand;
In_Person.Typing_Speed := In_Speed;
end Init_Data;
procedure Display(In_Person : SECRETARY) is
begin
for Index in 1.. In_Person.Name_Length loop
Put(In_Person.Name(Index));
end loop;
Put(" is a secretary that does ");
if not In_Person.Shorthand then
Put("not ");
end if;
Put("take shorthand.");
New_Line;
Put(" ");
for Index in 1..In_Person.Name_Length loop
Put(In_Person.Name(Index));
end loop;
Put(" is paid ");
Put(In_Person.Salary, 6);
Put(" dollars per year.");
New_Line;
end Display;
end Person.Positions;
-- Result of execution
--
-- (This package cannot be executed alone.)
-- Chapter 23 - Program 7
with Ada.Text_IO, Person, Person.Positions;
use Ada.Text_IO, Person, Person.Positions;
procedure Busines1 is
Big_John : SUPERVISOR;
Jessica : SUPERVISOR;
Steve : PROGRAMMER;
Patrick : PROGRAMMER;
Gwen : SECRETARY;
begin
Init_Data(Big_John, "John", 54000, "President");
Init_Data(Jessica, "Jessica", 47500, "CEO");
Init_Data(Steve, "Steve", 52000, "Chief Programmer", "Ada");
Init_Data(Patrick, "Patrick", 33000, "Assistant Debugger","C++");
Init_Data(Gwen, "Gwendolyn", 27000, TRUE, 85);
Display(Big_John);
Display(Jessica);
Display(Steve);
Display(Patrick);
Display(Gwen);
end Busines1;
-- Result of execution
--
-- John is a supervisor, and is the President of the company
-- Jessica is a supervisor, and is the CEO of the company
-- Steve is a programmer specializing in Ada. He makes 52000 dollars per year.
-- Patrick is a programmer specializing in C++. He makes 33000 dollars per year.
-- Gwendolyn is a secretary that does take shorthand.
-- Gwendolyn is paid 27000 dollars per year.
-- Chapter 23 - Program 8
with Ada.Text_IO, Person, Person.Positions;
use Ada.Text_IO, Person, Person.Positions;
procedure Busines2 is
Big_John : aliased SUPERVISOR;
Jessica : aliased SUPERVISOR;
Steve : aliased PROGRAMMER;
Patrick : aliased PROGRAMMER;
Gwen : aliased SECRETARY;
type EMPLOYEE_ACCESS is access all EMPLOYEE'Class;
Employee_Point : EMPLOYEE_ACCESS;
begin
Init_Data(Big_John, "John", 54000, "President");
Init_Data(Jessica, "Jessica", 47500, "CEO");
Init_Data(Steve, "Steve", 52000, "Chief Programmer", "Ada");
Init_Data(Patrick, "Patrick", 33000, "Assistant Debugger", "C++");
Init_Data(Gwen, "Gwendolyn", 27000, TRUE, 85);
Employee_Point := Big_John'Access;
Display(Employee_Point.all);
Employee_Point := Jessica'Access;
Display(Employee_Point.all);
Employee_Point := Steve'Access;
Display(Employee_Point.all);
Employee_Point := Patrick'Access;
Display(Employee_Point.all);
Employee_Point := Gwen'Access;
Display(Employee_Point.all);
end Busines2;
-- Result of execution
--
-- John is a supervisor, and is the President of the company
-- Jessica is a supervisor, and is the CEO of the company
-- Steve is a programmer specializing in Ada. He makes 52000 dollars per year.
-- Patrick is a programmer specializing in C++. He makes 33000 dollars per year.
-- Gwendolyn is a secretary that does take shorthand.
-- Gwendolyn is paid 27000 dollars per year.
-- Chapter 23 - Program 9
package Person is
type EMPLOYEE is abstract tagged private;
procedure Display(Person_In : EMPLOYEE) is abstract;
private
type EMPLOYEE is abstract tagged
record
Name : STRING(1..25);
Name_Length : INTEGER;
Salary : INTEGER;
end record;
end Person;
-- Result of execution
--
-- (This package cannot be executed alone.)
-- Chapter 23 - Program 10
with Ada.Strings.Bounded;
package Person is
type EMPLOYEE is abstract tagged private;
procedure Display(Person_In : EMPLOYEE) is abstract;

package My_Strings is new
Ada.Strings.Bounded.Generic_Bounded_Length(25);
use My_Strings;
private
type EMPLOYEE is abstract tagged
record
Name : BOUNDED_STRING;
Salary : INTEGER;
end record;
end Person;
-- Result of execution
--
-- (This package cannot be executed alone.)
-- Chapter 23 - Program 11
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
package Person.Positions is
-- The SUPERVISOR type has a title.
type SUPERVISOR is new EMPLOYEE with private;
procedure Init_Data(In_Person : in out SUPERVISOR;
In_Name : BOUNDED_STRING;
In_Salary : INTEGER;
In_Title : BOUNDED_STRING);
procedure Display(In_Person : SUPERVISOR);
-- The PROGRAMMER type has a language preference.
type PROGRAMMER is new EMPLOYEE with private;
procedure Init_Data(In_Person : in out PROGRAMMER;
In_Name : BOUNDED_STRING;
In_Salary : INTEGER;
In_Title : BOUNDED_STRING;
In_Language : BOUNDED_STRING);
procedure Display(In_Person : PROGRAMMER);
-- The SECRETARY type has a typing speed.
type SECRETARY is new EMPLOYEE with private;
procedure Init_Data(In_Person : in out SECRETARY;
In_Name : BOUNDED_STRING;
In_Salary : INTEGER;
In_ShortHand : BOOLEAN;
In_Speed : INTEGER);
procedure Display(In_Person : SECRETARY);
private
type SUPERVISOR is new EMPLOYEE with
record
Title : BOUNDED_STRING;
end record;
type PROGRAMMER is new EMPLOYEE with
record
Title : BOUNDED_STRING;
Language : BOUNDED_STRING;
end record;
type SECRETARY is new EMPLOYEE with
record
Shorthand : BOOLEAN;
Typing_Speed : INTEGER;
end record;

end Person.Positions;
package body Person.Positions is
-- Subprograms for the SUPERVISOR type.
procedure Init_Data(In_Person : in out SUPERVISOR;
In_Name : BOUNDED_STRING;
In_Salary : INTEGER;
In_Title : BOUNDED_STRING) is
begin
In_Person.Name := In_Name;
In_Person.Salary := In_Salary;
In_Person.Title := In_Title;
end Init_Data;
procedure Display(In_Person : SUPERVISOR) is
begin
for Index in 1..Length(In_Person.Name) loop
Put(Element(In_Person.Name, Index));
end loop;
Put(" is a supervisor, and is the ");
for Index in 1..Length(In_Person.Title) loop
Put(Element(In_Person.Title, Index));
end loop;
Put(" of the company");
New_Line;
end Display;
-- Subprograms for the PROGRAMMER type.
procedure Init_Data(In_Person : in out PROGRAMMER;
In_Name : BOUNDED_STRING;
In_Salary : INTEGER;
In_Title : BOUNDED_STRING;
In_Language : BOUNDED_STRING) is
begin
In_Person.Name := In_Name;
In_Person.Salary := In_Salary;
In_Person.Title := In_Title;
In_Person.Language := In_Language;
end Init_Data;
procedure Display(In_Person : PROGRAMMER) is
begin
for Index in 1..Length(In_Person.Name) loop
Put(Element(In_Person.Name, Index));
end loop;
Put(" is a programmer specializing in ");
for Index in 1..Length(In_Person.Language) loop
Put(Element(In_Person.Language, Index));
end loop;
Put(". He makes ");
Put(In_Person.Salary, 6);
Put(" dollars per year.");
New_Line;
end Display;
-- Subprograms for the SECRETARY type.
procedure Init_Data(In_Person : in out SECRETARY;
In_Name : BOUNDED_STRING;
In_Salary : INTEGER;
In_ShortHand : BOOLEAN;
In_Speed : INTEGER) is
begin
In_Person.name := In_Name;
In_Person.Salary := In_Salary;
In_Person.Shorthand := In_Shorthand;
In_Person.Typing_Speed := In_Speed;
end Init_Data;
procedure Display(In_Person : SECRETARY) is
begin
for Index in 1..Length(In_Person.Name) loop
Put(Element(In_Person.Name, Index));
end loop;
Put(" is a secretary that does ");
if not In_Person.Shorthand then
Put("not ");
end if;
Put("take shorthand.");
New_Line;
Put(" ");
for Index in 1..Length(In_Person.Name) loop
Put(Element(In_Person.Name, Index));
end loop;
Put(" is paid ");
Put(In_Person.Salary, 6);
Put(" dollars per year.");
New_Line;
end Display;
end Person.Positions;
-- Result of execution
--
-- (This package cannot be executed alone.)
-- Chapter 23 - Program 12
with Ada.Text_IO, Person, Person.Positions;
use Ada.Text_IO, Person, Person.Positions;
procedure Busines3 is
Big_John : SUPERVISOR;
Jessica : SUPERVISOR;
Steve : PROGRAMMER;
Patrick : PROGRAMMER;
Gwen : SECRETARY;
begin
Init_Data(Big_John, My_Strings.To_Bounded_String("John"),
54000, My_Strings.To_Bounded_String("President"));
Init_Data(Jessica, My_Strings.To_Bounded_String("Jessica"),
47500, My_Strings.To_Bounded_String("CEO"));
Init_Data(Steve, My_Strings.To_Bounded_String("Steve"),
52000, My_Strings.To_Bounded_String("Chief Programmer"),
My_Strings.To_Bounded_String("Ada"));
Init_Data(Patrick, My_Strings.To_Bounded_String("Patrick"),
33000, My_Strings.To_Bounded_String("Assistant Debugger"),
My_Strings.To_Bounded_String("C++"));
Init_Data(Gwen, My_Strings.To_Bounded_String("Gwendolyn"),
27000, TRUE, 85);
Display(Big_John);
Display(Jessica);
Display(Steve);
Display(Patrick);
Display(Gwen);
end Busines3;
-- Result of execution
--
-- John is a supervisor, and is the President of the company
-- Jessica is a supervisor, and is the CEO of the company
-- Steve is a programmer specializing in Ada. He makes 52000 dollars per year.
-- Patrick is a programmer specializing in C++. He makes 33000 dollars per year.
-- Gwendolyn is a secretary that does take shorthand.
-- Gwendolyn is paid 27000 dollars per year.
-- Chapter 23 - Exercise 1
with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO;
package CH23_1 is
-- A very simple transportation record.
type TRANSPORT is tagged private;
procedure Set_Values(Vehicle_In : in out TRANSPORT;
Wheels_In : INTEGER;
Weight_In : FLOAT);
function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER;
function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT;
-- Extend TRANSPORT to a CAR type.
type CAR is new TRANSPORT with private;
procedure Set_Values(Vehicle_In : in out CAR;
Passenger_Count_In : INTEGER);
function Get_Passenger_Count(Vehicle_In : CAR) return INTEGER;
-- Extend TRANSPORT to a TRUCK type.
type TRUCK is new TRANSPORT with private;
procedure Set_Values(Vehicle_In : in out TRUCK;
Wheels_In : INTEGER;
Weight_In : FLOAT;
Passenger_Count_In : INTEGER;
Payload_In : FLOAT);
function Get_Passenger_Count(Vehicle_In : TRUCK) return INTEGER;
-- Derive an identical type for BICYCLE.
type BICYCLE is new TRANSPORT with private;
-- Print_Values is a class-wide operation. It can accept objects
-- of any type within the TRANSPORT heirarchy.
procedure Print_Values(Any_Vehicle : TRANSPORT'Class);
private
type TRANSPORT is tagged
record
Wheels : INTEGER;
Weight : FLOAT;
end record;
type CAR is new TRANSPORT with
record
Passenger_Count : INTEGER;
end record;
type TRUCK is new TRANSPORT with
record
Passenger_Count : INTEGER;
Payload : FLOAT;
end record;
type BICYCLE is new TRANSPORT with null record;
end CH23_1;
package body CH23_1 is
-- Subprograms for the TRANSPORT record
procedure Set_Values(Vehicle_In : in out TRANSPORT;
Wheels_In : INTEGER;
Weight_In : FLOAT) is
begin
Vehicle_In.Wheels := Wheels_In;
Vehicle_In.Weight := Weight_In;
end Set_Values;
function Get_Wheels(Vehicle_In : TRANSPORT) return INTEGER is
begin
return Vehicle_In.Wheels;
end Get_Wheels;
function Get_Weight(Vehicle_In : TRANSPORT) return FLOAT is
begin
return Vehicle_In.Weight;
end Get_Weight;
-- Subprograms for the CAR record
procedure Set_Values(Vehicle_In : in out CAR;
Passenger_Count_In : INTEGER) is
begin
Vehicle_In.Passenger_Count := Passenger_Count_In;
end Set_Values;
function Get_Passenger_Count(Vehicle_In : CAR) return INTEGER is
begin
return Vehicle_In.Passenger_Count;
end Get_Passenger_Count;
-- Subprograms for the TRUCK record
procedure Set_Values(Vehicle_In : in out TRUCK;
Wheels_In : INTEGER;
Weight_In : FLOAT;
Passenger_Count_In : INTEGER;
Payload_In : FLOAT) is
begin
-- This is one way to set the values in the base class
Vehicle_In.Wheels := Wheels_In;
Vehicle_In.Weight := Weight_In;
-- This is another way to set the values in the base class
Set_Values(TRANSPORT(Vehicle_In), Wheels_In, Weight_In);
-- This sets the values in this class
Vehicle_In.Passenger_Count := Passenger_Count_In;
Vehicle_In.Payload := Payload_In;
end Set_Values;
function Get_Passenger_Count(Vehicle_In : TRUCK) return INTEGER is
begin
return Vehicle_In.Passenger_Count;
end Get_Passenger_Count;
-- Print_Values is a class-wide operation. It can accept objects
-- of any type within the TRANSPORTheirarchy.
procedure Print_Values(Any_Vehicle : TRANSPORT'Class) is
begin
Put("This vehicle has");
Put(Any_Vehicle.Wheels, 2);
Put(" wheels, and weighs");
Put(Any_Vehicle.Weight, 5, 1, 0);
Put(" pounds.");
New_Line;
-- The following line of code will produce an error because TRANSPORT
-- and BICYCLE do not contain this variable.
Put(Any_Vehicle.Passenger_Count, 5);
end Print_Values;
end CH23_1;
-- Result of execution
--
-- (This program cannot be executed alone.)
-- Chapter 23 - Exercise 2
with Ada.Text_IO, Person, Person.Positions;
use Ada.Text_IO, Person, Person.Positions;
procedure CH23_2 is
Big_John : aliased SUPERVISOR;
Jessica : aliased SUPERVISOR;
Steve : aliased PROGRAMMER;
Patrick : aliased PROGRAMMER;
Gwen : aliased SECRETARY;
Willy : EMPLOYEE;
type EMPLOYEE_ACCESS is access all EMPLOYEE'Class;
Employee_Point : EMPLOYEE_ACCESS;
begin
Init_Data(Big_John, "John", 54000, "President");
Init_Data(Jessica, "Jessica", 47500, "CEO");
Init_Data(Steve, "Steve", 52000, "Chief Programmer", "Ada");
Init_Data(Patrick, "Patrick", 33000, "Assistant Debugger", "C++");
Init_Data(Gwen, "Gwendolyn", 27000, TRUE, 85);
Display(Willy);
Employee_Point := Big_John'Access;
Display(Employee_Point.all);
Employee_Point := Jessica'Access;
Display(Employee_Point.all);
Employee_Point := Steve'Access;
Display(Employee_Point.all);
Employee_Point := Patrick'Access;
Display(Employee_Point.all);
Employee_Point := Gwen'Access;
Display(Employee_Point.all);
end CH23_2;
-- Result of execution
--
-- John is a supervisor, and is the President of the company
-- Jessica is a supervisor, and is the CEO of the company
-- Steve is a programmer specializing in Ada. He makes 52000 dollars per year.
-- Patrick is a programmer specializing in C++. He makes 33000 dollars per year.
-- Gwendolyn is a secretary that does take shorthand.
-- Gwendolyn is paid 27000 dollars per year.
-- Chapter 23 - Exercise 3
with Ada.Text_IO, Person, Person.Positions;
use Ada.Text_IO, Person, Person.Positions;
procedure CH23_3 is
Big_John : aliased SUPERVISOR;
Jessica : aliased SUPERVISOR;
Steve : aliased PROGRAMMER;
Patrick : aliased PROGRAMMER;
Gwen : aliased SECRETARY;
Willy : EMPLOYEE;
type EMPLOYEE_ACCESS is access all EMPLOYEE'Class;
Employee_Point : EMPLOYEE_ACCESS;
begin
Init_Data(Big_John, "John", 54000, "President");
Init_Data(Jessica, "Jessica", 47500, "CEO");
Init_Data(Steve, "Steve", 52000, "Chief Programmer", "Ada");
Init_Data(Patrick, "Patrick", 33000, "Assistant Debugger", "C++");
Init_Data(Gwen, "Gwendolyn", 27000, TRUE, 85);
Employee_Point := Big_John'Access;
Display(Employee_Point.all);
Employee_Point := Jessica'Access;
Display(Employee_Point.all);
Employee_Point := Steve'Access;
Display(Employee_Point.all);
Employee_Point := Patrick'Access;
Display(Employee_Point.all);
Employee_Point := Gwen'Access;
Display(Employee_Point.all);
end CH23_3;
-- Result of execution
--
-- John is a supervisor, and is the President of the company
-- Jessica is a supervisor, and is the CEO of the company
-- Steve is a programmer specializing in Ada. He makes 52000 dollars per year.
-- Patrick is a programmer specializing in C++. He makes 33000 dollars per year.
-- Gwendolyn is a secretary that does take shorthand.
-- Gwendolyn is paid 27000 dollars per year.
-- Chapter 24 - Program 1
with Ada.Text_IO;
use Ada.Text_IO;
with Ada.Sequential_IO;
procedure BiSeqOut is
type MY_REC is
record
Age : INTEGER;
Sex : CHARACTER;
Initial : CHARACTER;
end record;
package Seq_IO is new Ada.Sequential_IO(MY_REC);
use Seq_IO;
Myself : MY_REC;
My_Out_File : Seq_IO.FILE_TYPE;
begin
Create(My_Out_File, Out_File, "NAMEFILE.TXT");
Myself.Sex := 'M';
Myself.Initial := 'X';
for Index in 1..100 loop
Myself.Age := Index;
Write(My_Out_File, Myself);
end loop;
Close(My_Out_File);
end BiSeqOut;
-- Result of Execution
-- (The only output is a binary file named NAMEFILE.TXT)
-- Chapter 24 - Program 2
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
with Ada.Sequential_IO;
procedure BiSeqIn is
type MY_REC is
record
Age : INTEGER;
Sex : CHARACTER;
Initial : CHARACTER;
end record;
package Seq_IO is new Ada.Sequential_IO(MY_REC);
use Seq_IO;
Myself : MY_REC;
My_In_File : Seq_IO.FILE_TYPE;
begin
Open(My_In_File, In_File, "NAMEFILE.TXT");
for Index in 1..100 loop
Read(My_In_File, Myself);
if Myself.Age >= 82 then
Put("Record number");
Put(Myself.Age, 4);
Put(" ");
Put(Myself.Sex);
Put(" ");
Put(Myself.Initial);
New_Line;
end if;
end loop;
Close(My_In_File);
end BiSeqIn;
-- Result of Execution
-- Record number 82 M X
-- Record number 83 M X
-- Record number 84 M X
-- Record number 85 M X
-- Record number 86 M X
-- Record number 87 M X
-- Record number 88 M X
-- Record number 89 M X
-- Record number 90 M X
-- Record number 91 M X
-- Record number 92 M X
-- Record number 93 M X
-- Record number 94 M X
-- Record number 95 M X
-- Record number 96 M X
-- Record number 97 M X
-- Record number 98 M X
-- Record number 99 M X
-- Record number 100 M X
-- Chapter 24 - Program 3
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
with Ada.Direct_IO;
procedure BiRandIO is
type MY_REC is
record
Age : INTEGER;
Sex : CHARACTER;
Initial : CHARACTER;
end record;
package Ran_IO is new Ada.Direct_IO(MY_REC);
use Ran_IO;
Myself : MY_REC;
My_In_Out_File : Ran_IO.FILE_TYPE;
procedure Display_Record(In_Rec : MY_REC) is
begin
Put("Record number");
Put(In_Rec.Age, 4);
Put(" ");
Put(In_Rec.Sex);
Put(" ");
Put(In_Rec.Initial);
New_Line;
end Display_Record;
begin
Open(My_In_Out_File, InOut_File, "NAMEFILE.TXT");
Read(My_In_Out_File, Myself, 37);
Display_Record(Myself);
Read(My_In_Out_File, Myself, 25);
Display_Record(Myself);
Read(My_In_Out_File, Myself);
Display_Record(Myself);
New_Line;
Myself.Age := 33;
Myself.Sex := 'F';
Myself.Initial := 'Z';
Write(My_In_Out_File, Myself, 91);
Write(My_In_Out_File, Myself, 96);
Write(My_In_Out_File, Myself);
Set_Index(My_In_Out_File, 88);
while not End_Of_File(My_In_Out_File) loop
Read(My_In_Out_File, Myself);
Display_Record(Myself);
end loop;
Close(My_In_Out_File);
end BiRandIO;
-- Result of Execution
-- Record number 37 M X
-- Record number 25 M X
-- Record number 26 M X
-- Record number 88 M X
-- Record number 89 M X
-- Record number 90 M X
-- Record number 33 F Z
-- Record number 92 M X
-- Record number 93 M X
-- Record number 94 M X
-- Record number 95 M X
-- Record number 33 F Z
-- Record number 33 F Z
-- Record number 98 M X
-- Record number 99 M X
-- Record number 100 M X
-- Chapter 24 - Program 4
with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Streams.Stream_IO;
use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Streams.Stream_IO;
procedure Stream is
My_File : Ada.Streams.Stream_IO.FILE_TYPE;
My_File_Access : Ada.Streams.Stream_IO.STREAM_ACCESS;
type RESULT is (WIN, LOSE, TIE, FORFEIT);
type BOX is
record
Length : INTEGER;
Width : INTEGER;
Height : INTEGER;
end record;
Big_Box, Small_Box : BOX := (7, 8, 9);
Football, Baseball : RESULT := TIE;
Dogs, Pigs, Cats : INTEGER := 27;
Animal : INTEGER := 100;
begin
-- Create a stream to a file in the output mode to write to.
Ada.Streams.Stream_IO.Create(My_File, Out_File, "funny.txt");
My_File_Access := Ada.Streams.Stream_IO.Stream(My_File);

INTEGER'Write(My_File_Access, Dogs);
BOX'Write(My_File_Access, Big_Box);
BOX'Write(My_File_Access, Small_Box);
RESULT'Write(My_File_Access, Baseball);
INTEGER'Write(My_File_Access, Pigs);
RESULT'Write(My_File_Access, Football);
INTEGER'Write(My_File_Access, Cats);
Ada.Streams.Stream_IO.Close(My_File);
-- Open the stream in the input mode to read from it.
Open(My_File, In_File, "funny.txt");
My_File_Access := Stream(My_File);
INTEGER'Read(My_File_Access, Animal);
BOX'Read(My_File_Access, Small_Box);
-- The others can be read here too
Put("Animal has a value of ");
Put(Animal, 6);
New_Line;
Close(My_File);
end Stream;
-- Result of execution
-- Animal has a value of 27
-- Chapter 24 - Programming exercise 1
with Ada.Text_IO;
use Ada.Text_IO;
with Ada.Sequential_IO;
procedure CH24_1 is
type MY_REC is
record
Age : INTEGER;
Sex : CHARACTER;
Initial : CHARACTER;
end record;
package Seq_IO is new Ada.Sequential_IO(MY_REC);
use Seq_IO;
Myself : MY_REC;
My_Out_File : Seq_IO.FILE_TYPE;
Other_File : Seq_IO.FILE_TYPE;
begin
Create(My_Out_File, Out_File, "NAMEFILE.TXT");
Create(Other_File, Out_File, "OTHRFILE.TXT");
Myself.Sex := 'M';
for Index in 1..100 loop
Myself.Age := Index;
Myself.Initial := 'X';
Write(My_Out_File, Myself);
if (Myself.Age >= 50) and (Myself.Age <= 60) then
Myself.Initial := 'O';
Write(Other_File, Myself);
else
Write(Other_File, Myself);
end if;
end loop;
Close(My_Out_File);
Close(Other_File);
end CH24_1;
-- Result of Execution
-- (The output is a binary file named NAMEFILE.TXT,
-- and a binary file named OTHRFILE.TXT.)
-- Chapter 24 - Programming exercise 2
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
with Ada.Sequential_IO;
procedure CH24_2 is
type MY_REC is
record
Age : INTEGER;
Sex : CHARACTER;
Initial : CHARACTER;
end record;
package Seq_IO is new Ada.Sequential_IO(MY_REC);
use Seq_IO;
Myself, Other : MY_REC;
My_In_File : Seq_IO.FILE_TYPE;
Other_File : Seq_IO.FILE_TYPE;
begin
Open(My_In_File, In_File, "NAMEFILE.TXT");
Open(Other_File, In_File, "OTHRFILE.TXT");
for Index in 1..100 loop
Read(My_In_File, Myself);
Read(Other_File, Other);
if (Myself.Age /= Other.Age) or
(Myself.Sex /= Other.Sex) or
(Myself.Initial /= Other.Initial) then
Put("Record number");
Put(Myself.Age);
Put(" ");
Put(Myself.Sex);
Put(" ");
Put(Myself.Initial);
Put(Other.Age,12);
Put(" ");
Put(Other.Sex);
Put(" ");
Put(Other.Initial);
New_Line;
end if;
end loop;
Close(My_In_File);
Close(Other_File);
end CH24_2;
-- Result of Execution
-- Record number 50 M X 50 M O
-- Record number 51 M X 51 M O
-- Record number 52 M X 52 M O
-- Record number 53 M X 53 M O
-- Record number 54 M X 54 M O
-- Record number 55 M X 55 M O
-- Record number 56 M X 56 M O
-- Record number 57 M X 57 M O
-- Record number 58 M X 58 M O
-- Record number 59 M X 59 M O
-- Record number 60 M X 60 M O
-- Chapter 24 - Programming exercise 3
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
with Ada.Sequential_IO;
procedure CH24_3 is
type MY_REC is
record
Age : INTEGER;
Sex : CHARACTER;
Initial : CHARACTER;
end record;
package Seq_IO is new Ada.Sequential_IO(MY_REC);
use Seq_IO;
Myself : MY_REC;
My_Out_File : Seq_IO.FILE_TYPE;
My_In_File : Seq_IO.FILE_TYPE;
begin
Create(My_Out_File, Out_File, "NAMEFILE.TXT");
Myself.Sex := 'M';
Myself.Initial := 'X';
for Index in 1..100 loop
Myself.Age := Index;
Write(My_Out_File, Myself);
end loop;
Close(My_Out_File);
Open(My_In_File, In_File, "NAMEFILE.TXT");
for Index in 1..100 loop
Read(My_In_File, Myself);
if Myself.Age >= 82 then
Put("Record number");
Put(Myself.Age);
Put(" ");
Put(Myself.Sex);
Put(" ");
Put(Myself.Initial);
New_Line;
end if;
end loop;
Close(My_In_File);
end CH24_3;
-- Result of Execution
-- Record number 82 M X
-- Record number 83 M X
-- Record number 84 M X
-- Record number 85 M X
-- Record number 86 M X
-- Record number 87 M X
-- Record number 88 M X
-- Record number 89 M X
-- Record number 90 M X
-- Record number 91 M X
-- Record number 92 M X
-- Record number 93 M X
-- Record number 94 M X
-- Record number 95 M X
-- Record number 96 M X
-- Record number 97 M X
-- Record number 98 M X
-- Record number 99 M X
-- Record number 100 M X
-- Chapter 25 - Program 1
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure LinkList is
Data_String : constant STRING := "This tests ADA";
type CHAR_REC; -- Incomplete declaration
type CHAR_REC_POINT is access CHAR_REC;
type CHAR_REC is -- Complete declaration
record
One_Letter : CHARACTER;
Next_Rec : CHAR_REC_POINT;
end record;
Start : CHAR_REC_POINT; -- Always points to start of list
Last : CHAR_REC_POINT; -- Points to the end of the list
procedure Traverse_List(Starting_Point : CHAR_REC_POINT) is
Temp : CHAR_REC_POINT; -- Moves through the list
begin
Put("In traverse routine. --->");
Temp := Starting_Point;
if Temp = null then
Put("No data in list.");
else
loop
Put(Temp.One_Letter);
Temp := Temp.Next_Rec;
if Temp = null then exit; end if;
end loop;
end if;
New_Line;
end Traverse_List;
procedure Store_Character(In_Char : CHARACTER) is
Temp : CHAR_REC_POINT;
begin
Temp := new CHAR_REC;
Temp.One_Letter := In_Char; -- New record is now defined
-- The system sets Next_Rec
-- to the value of null
if Start = null then
Start := Temp;
Last := Temp;
else
Last.Next_Rec := Temp;
Last := Temp;
end if;
Traverse_List(Start);
end Store_Character;
begin
-- Store the characters in Data_String in a linked list
for Index in Data_String'RANGE loop
Store_Character(Data_String(Index));
end loop;
-- Traverse the final list
New_Line;
Put_Line("Now for the final traversal.");
Traverse_List(Start);
-- Free the entire list now
loop
exit when Start = null;
Last := Start.Next_Rec;
Start.Next_Rec := null;
Start := Last;
end loop;
end LinkList;
-- Result of execution
-- In traverse routine. --->T
-- In traverse routine. --->Th
-- In traverse routine. --->Thi
-- In traverse routine. --->This
-- In traverse routine. --->This
-- In traverse routine. --->This t
-- In traverse routine. --->This te
-- In traverse routine. --->This tes
-- In traverse routine. --->This test
-- In traverse routine. --->This tests
-- In traverse routine. --->This tests
-- In traverse routine. --->This tests A
-- In traverse routine. --->This tests AD
-- In traverse routine. --->This tests ADA
--
-- Now for the final traversal.
-- In traverse routine. --->This tests ADA
-- Chapter 25 - Program 2
with Ada.Text_IO, Ada.Integer_Text_IO, Unchecked_Deallocation;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure SortList is
Data_String : constant STRING := "This tests ADA";
type CHAR_REC; -- Incomplete declaration
type CHAR_REC_POINT is access CHAR_REC;
type CHAR_REC is -- Complete declaration
record
One_Letter : CHARACTER;
Next_Rec : CHAR_REC_POINT;
end record;
Start : CHAR_REC_POINT; -- Always points to start of list
Last : CHAR_REC_POINT; -- Points to the end of the list
procedure Free is new Unchecked_Deallocation(CHAR_REC,
CHAR_REC_POINT);
pragma Controlled(CHAR_REC_POINT);
procedure Traverse_List(Starting_Point : CHAR_REC_POINT) is
Temp : CHAR_REC_POINT; -- Moves through the list
begin
Put("In traverse routine. --->");
Temp := Starting_Point;
if Temp = null then
Put("No data in list.");
else
loop
Put(Temp.One_Letter);
Temp := Temp.Next_Rec;
if Temp = null then exit; end if;
end loop;
end if;
New_Line;
end Traverse_List;
procedure Store_Character(In_Char : CHARACTER) is
Temp : CHAR_REC_POINT; -- Moves through the list
procedure Locate_And_Store is
Search : CHAR_REC_POINT;
Prior : CHAR_REC_POINT;
begin
Search := Start;
while In_Char > Search.One_Letter loop
Prior := Search;
Search := Search.Next_Rec;
if Search = null then exit; end if;
end loop;
if Search = Start then -- New character at head of list
Temp.Next_Rec := Start;
Start := Temp;
elsif Search = null then -- New character at tail of list
Last.Next_Rec := Temp;
Last := Temp;
else -- New character within list
Temp.Next_Rec := Search;
Prior.Next_Rec := Temp;
end if;
end Locate_And_Store;
begin
Temp := new CHAR_REC;
Temp.One_Letter := In_Char; -- New record is now defined
-- The system sets Next_Rec
-- to the value of null
if Start = null then
Start := Temp;
Last := Temp;
else
Locate_And_Store;
end if;
Traverse_List(Start);
end Store_Character;
begin
-- Store the characters in Data_String in a linked list
for Index in Data_String'RANGE loop
Store_Character(Data_String(Index));
end loop;
-- Traverse the final list
New_Line;
Put_Line("Now for the final traversal.");
Traverse_List(Start);
-- Deallocate the list now
loop
exit when Start = null;
Last := Start.Next_Rec;
Free(Start);
Start := Last;
end loop;
end SortList;
-- Result of execution
--
-- In traverse routine. --->T
-- In traverse routine. --->Th
-- In traverse routine. --->Thi
-- In traverse routine. --->This
-- In traverse routine. ---> This
-- In traverse routine. ---> Thist
-- In traverse routine. ---> Tehist
-- In traverse routine. ---> Tehisst
-- In traverse routine. ---> Tehisstt
-- In traverse routine. ---> Tehissstt
-- In traverse routine. ---> Tehissstt
-- In traverse routine. ---> ATehissstt
-- In traverse routine. ---> ADTehissstt
-- In traverse routine. ---> AADTehissstt
--
-- Now for the final traversal.
-- In traverse routine. ---> AADTehissstt
-- Chapter 25 - Program 3
with Ada.Text_IO, Unchecked_Deallocation;
use Ada.Text_IO;
procedure BTree is
Test_String : constant STRING := "DBCGIF";
Data_String : constant STRING := "This tests ADA";
type B_TREE_NODE; -- Incomplete declaration
type NODE_POINT is access B_TREE_NODE;
type B_TREE_NODE is -- Complete declaration
record
One_Letter : CHARACTER;
Left : NODE_POINT;
Right : NODE_POINT;
end record;
procedure Free is new Unchecked_Deallocation(B_TREE_NODE,
NODE_POINT);
pragma Controlled(NODE_POINT);
Root : NODE_POINT; -- Always points to the root of the tree
procedure Traverse_List(Start_Node : NODE_POINT) is
begin
if Start_Node.Left /= null then
Traverse_List(Start_Node.Left);
end if;
Put(Start_Node.One_Letter);
if Start_Node.Right /= null then
Traverse_List(Start_Node.Right);
end if;
end Traverse_List;
procedure Store_Character(In_Char : CHARACTER) is
Temp : NODE_POINT;
procedure Locate_And_Store(Begin_Node : in out NODE_POINT) is
begin
if In_Char < Begin_Node.One_Letter then
if Begin_Node.Left = null then
Begin_Node.Left := Temp;
else
Locate_And_Store(Begin_Node.Left);
end if;
else
if Begin_Node.Right = null then
Begin_Node.Right := Temp;
else
Locate_And_Store(Begin_Node.Right);
end if;
end if;
end Locate_And_Store;
begin
Temp := new B_TREE_NODE;
Temp.One_Letter := In_Char; -- New record is now defined
-- The system sets Next_Rec
-- to the value of null
if Root = null then
Root := Temp;
else
Locate_And_Store(Root);
end if;
Put("Ready to traverse list. --->");
Traverse_List(Root);
New_Line;
end Store_Character;
begin
-- Store the characters in Data_String in a Binary Tree
for Index in Data_String'RANGE loop
Store_Character(Data_String(Index));
end loop;
-- Traverse the list
New_Line;
Put_Line("Now for the final traversal of Data_String.");
Put("Ready to traverse list. --->");
Traverse_List(Root);
New_Line(2);
Root := null; -- Needed to clear out the last tree
-- Store the characters in Test_String in a Binary Tree
for Index in Test_String'RANGE loop
Store_Character(Test_String(Index));
end loop;
-- Traverse the list
New_Line;
Put_Line("Now for the final traversal of Test_String.");
Put("Ready to traverse list. --->");
Traverse_List(Root);
New_Line;
-- Now deallocate the tree
declare
procedure Free_Up(Current_Node : in out NODE_POINT) is
begin
if Current_Node.Left /= null then
Free_Up(Current_Node.Left);
end if;
if Current_Node.Right /= null then
Free_Up(Current_Node.Right);
end if;
Free(Current_Node);
end Free_Up;
begin
if Root /= null then
Free_Up(Root);
end if;
end;
end BTree;
-- Result of execution
--
-- Ready to traverse list. --->T
-- Ready to traverse list. --->Th
-- Ready to traverse list. --->Thi
-- Ready to traverse list. --->This
-- Ready to traverse list. ---> This
-- Ready to traverse list. ---> Thist
-- Ready to traverse list. ---> Tehist
-- Ready to traverse list. ---> Tehisst
-- Ready to traverse list. ---> Tehisstt
-- Ready to traverse list. ---> Tehissstt
-- Ready to traverse list. ---> Tehissstt
-- Ready to traverse list. ---> ATehissstt
-- Ready to traverse list. ---> ADTehissstt
-- Ready to traverse list. ---> AADTehissstt
--
-- Now for the final traversal of Data_String.
-- Ready to traverse list. ---> AADTehissstt
-- Ready to traverse list. --->D
-- Ready to traverse list. --->BD
-- Ready to traverse list. --->BCD
-- Ready to traverse list. --->BCDG
-- Ready to traverse list. --->BCDGI
-- Ready to traverse list. --->BCDFGI
-- Now for the final traversal of Test_String.
-- Ready to traverse list. --->BCDFGI
-- Chapter 25 - Programming exercise 1
with Ada.Text_IO, Ada.Integer_Text_IO, Unchecked_Deallocation;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure CH25_1 is
Data_String : constant STRING := "This tests ADA";
type CHAR_REC; -- Incomplete declaration
type CHAR_REC_POINT is access CHAR_REC;
type CHAR_REC is -- Complete declaration
record
One_Letter : CHARACTER;
Next_Rec : CHAR_REC_POINT;
end record;
Start : CHAR_REC_POINT; -- Always points to start of list
Last : CHAR_REC_POINT; -- Points to the end of the list
procedure Free is new
Unchecked_Deallocation(CHAR_REC,CHAR_REC_POINT);
procedure Traverse_List(Starting_Point : CHAR_REC_POINT) is
Temp : CHAR_REC_POINT; -- Moves through the list
begin
Put("In traverse routine. --->");
Temp := Starting_Point;
if Temp = null then
Put("No data in list.");
else
loop
Put(Temp.One_Letter);
Temp := Temp.Next_Rec;
if Temp = null then exit; end if;
end loop;
end if;
New_Line;
end Traverse_List;
procedure Store_Character(In_Char : CHARACTER) is
Temp : CHAR_REC_POINT;
begin
Temp := new CHAR_REC;
Temp.One_Letter := In_Char; -- New record is now defined
-- The system sets Next_Rec
-- to the value of null
if Start = null then
Start := Temp;
Last := Temp;
else
Last.Next_Rec := Temp;
Last := Temp;
end if;
Traverse_List(Start);
end Store_Character;
begin
-- Store the characters in Data_String in a linked list
for Index in Data_String'RANGE loop
Store_Character(Data_String(Index));
end loop;
-- Traverse the final list
New_Line;
Put_Line("Now for the final traversal.");
Traverse_List(Start);
-- Free the entire list now
loop
exit when Start = null;
Last := Start.Next_Rec;
Free(Start);
Start := Last;
end loop;
end CH25_1;
-- Result of execution
-- In traverse routine. --->T
-- In traverse routine. --->Th
-- In traverse routine. --->Thi
-- In traverse routine. --->This
-- In traverse routine. --->This
-- In traverse routine. --->This t
-- In traverse routine. --->This te
-- In traverse routine. --->This tes
-- In traverse routine. --->This test
-- In traverse routine. --->This tests
-- In traverse routine. --->This tests
-- In traverse routine. --->This tests A
-- In traverse routine. --->This tests AD
-- In traverse routine. --->This tests ADA
--
-- Now for the final traversal.
-- In traverse routine. --->This tests ADA
-- Chapter 25 - Programming exercise 2
with Ada.Text_IO, Ada.Integer_Text_IO, Unchecked_Deallocation;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure CH25_2 is
Test_String : constant STRING := "DBCGIF";
Data_String : constant STRING := "This tests ADA";
type B_TREE_NODE; -- Incomplete declaration
type NODE_POINT is access B_TREE_NODE;
type B_TREE_NODE is -- Complete declaration
record
One_Letter : CHARACTER;
Character_Count : INTEGER;
Left : NODE_POINT;
Right : NODE_POINT;
end record;
pragma CONTROLLED(NODE_POINT);
procedure Free is new Unchecked_Deallocation(B_TREE_NODE,
NODE_POINT);
Root : NODE_POINT; -- Always points to the root of the tree
procedure Final_Traverse_List(Start_Node : NODE_POINT) is
begin
if Start_Node.Left /= null then
Final_Traverse_List(Start_Node.Left);
end if;
Put(Start_Node.One_Letter);
Put(" is character");
Put(Start_Node.Character_Count,3);
Put_Line(" in the string.");
if Start_Node.Right /= null then
Final_Traverse_List(Start_Node.Right);
end if;
end Final_Traverse_List;
procedure Traverse_List(Start_Node : NODE_POINT) is
begin
if Start_Node.Left /= null then
Traverse_List(Start_Node.Left);
end if;
Put(Start_Node.One_Letter);
if Start_Node.Right /= null then
Traverse_List(Start_Node.Right);
end if;
end Traverse_List;
procedure Store_Character(Char_Count : INTEGER;
In_Char : CHARACTER) is
Temp : NODE_POINT;
procedure Locate_And_Store(Begin_Node : in out NODE_POINT) is
begin
if In_Char < Begin_Node.One_Letter then
if Begin_Node.Left = null then
Begin_Node.Left := Temp;
else
Locate_And_Store(Begin_Node.Left);
end if;
else
if Begin_Node.Right = null then
Begin_Node.Right := Temp;
else
Locate_And_Store(Begin_Node.Right);
end if;
end if;
end Locate_And_Store;
begin
Temp := new B_TREE_NODE;
Temp.One_Letter := In_Char; -- New record is now defined
-- The system sets Next_Rec
-- to the value of null
Temp.Character_Count := Char_Count;
if Root = null then
Root := Temp;
else
Locate_And_Store(Root);
end if;
Put("Ready to traverse list. --->");
Traverse_List(Root);
New_Line;
end Store_Character;
begin
-- Store the characters in Data_String in a Binary Tree
for Index in Data_String'RANGE loop
Store_Character(Index,Data_String(Index));
end loop;
-- Traverse the list
New_Line;
Put_Line("Now for the final traversal of Data_String.");
Final_Traverse_List(Root);
New_Line(2);
Root := null; -- Needed to clear out the last tree
-- Store the characters in Test_String in a Binary Tree
for Index in Test_String'RANGE loop
Store_Character(Index,Test_String(Index));
end loop;
-- Traverse the list
New_Line;
Put_Line("Now for the final traversal of Test_String.");
Final_Traverse_List(Root);
New_Line;
-- Now deallocate the tree
declare
procedure Free_Up(Current_Node : in out NODE_POINT) is
begin
if Current_Node.Left /= null then
Free_Up(Current_Node.Left);
end if;
if Current_Node.Right /= null then
Free_Up(Current_Node.Right);
end if;
Free(Current_Node);
end Free_Up;
begin
if Root /= null then
Free_Up(Root);
end if;
end;
end CH25_2;
-- Result of execution
--
-- Ready to traverse list. --->T
-- Ready to traverse list. --->Th
-- Ready to traverse list. --->Thi
-- Ready to traverse list. --->This
-- Ready to traverse list. ---> This
-- Ready to traverse list. ---> Thist
-- Ready to traverse list. ---> Tehist
-- Ready to traverse list. ---> Tehisst
-- Ready to traverse list. ---> Tehisstt
-- Ready to traverse list. ---> Tehissstt
-- Ready to traverse list. ---> Tehissstt
-- Ready to traverse list. ---> ATehissstt
-- Ready to traverse list. ---> ADTehissstt
-- Ready to traverse list. ---> AADTehissstt
--
-- Now for the final traversal of Data_String.
-- is character 5 in the string.
-- is character 11 in the string.
-- A is character 12 in the string.
-- A is character 14 in the string.
-- D is character 13 in the string.
-- T is character 1 in the string.
-- e is character 7 in the string.
-- h is character 2 in the string.
-- i is character 3 in the string.
-- s is character 4 in the string.
-- s is character 8 in the string.
-- s is character 10 in the string.
-- t is character 6 in the string.
-- t is character 9 in the string.
-- Ready to traverse list. --->D
-- Ready to traverse list. --->BD
-- Ready to traverse list. --->BCD
-- Ready to traverse list. --->BCDG
-- Ready to traverse list. --->BCDGI
-- Ready to traverse list. --->BCDFGI
-- Now for the final traversal of Test_String.
-- B is character 2 in the string.
-- C is character 3 in the string.
-- D is character 1 in the string.
-- F is character 6 in the string.
-- G is character 4 in the string.
-- I is character 5 in the string.
-- Chapter 26 - Program 1
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
with Ada.Calendar; use Ada.Calendar;
procedure Timer is
package Fix_IO is new Ada.Text_IO.Fixed_IO(DAY_DURATION);
use Fix_IO;
Year,Month,Day : INTEGER;
Start,Seconds : DAY_DURATION;
Time_And_Date : TIME;
begin
Put_Line("Begin 3.14 second delay");
delay 3.14;
Put_Line("End of 3.14 second delay");
Time_And_Date := Clock;
Split(Time_And_Date, Year, Month, Day, Start); -- get start time
for Index in 1..9 loop
Put("The date and time are now");
Time_And_Date := Clock;
Split(Time_And_Date, Year, Month, Day, Seconds);
Put(Month, 3);
delay 0.2;
Put(Day, 3);
delay 0.1;
Put(Year, 5);
delay 0.1;
Put(Seconds - Start, 8, 3, 0);
New_Line;
delay 0.6;
end loop;
Put_Line("Begin non-accumulative timing loop here.");
Time_And_Date := Clock;
Split(Time_And_Date, Year, Month, Day, Start); -- get start time
for Index in 1..9 loop
Time_And_Date := Clock;
Split(Time_And_Date, Year, Month, Day, Seconds);
Put("The elapsed time is");
Put(Seconds - Start, 8, 3, 0);
New_Line;
delay DAY_DURATION(Index) - (Seconds - Start);
end loop;
Time_And_Date := Clock;
for Index in 1..12 loop
Time_And_Date := Time_And_Date + 0.4;
delay until Time_And_Date;
Put("Tick ");
end loop;
New_Line;
end Timer;
-- Result of Execution
-- Begin 3.14 second delay
-- End of 3.14 second delay
-- The date and time are now 3 22 1997 0.000
-- The date and time are now 3 22 1997 1.090
-- The date and time are now 3 22 1997 2.140
-- The date and time are now 3 22 1997 3.180
-- The date and time are now 3 22 1997 4.230
-- The date and time are now 3 22 1997 5.270
-- The date and time are now 3 22 1997 6.320
-- The date and time are now 3 22 1997 7.360
-- The date and time are now 3 22 1997 8.400
-- Begin non-accumulative timing loop here
-- The elapsed time is 0.000
-- The elapsed time is 1.100
-- The elapsed time is 2.030
-- The elapsed time is 3.020
-- The elapsed time is 4.040
-- The elapsed time is 5.030
-- The elapsed time is 6.020
-- The elapsed time is 7.010
-- The elapsed time is 8.000
-- Tick Tick Tick Tick Tick Tick Tick Tick Tick Tick Tick Tick
-- Chapter 26 - Program 2
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Task1 is
task First_Task;
task body First_Task is
begin
for Index in 1..4 loop
Put("This is in First_Task, pass number ");
Put(Index, 3);
New_Line;
end loop;
end First_Task;
task Second_Task;
task body Second_Task is
begin
for Index in 1..7 loop
Put("This is in Second_Task, pass number");
Put(Index, 3);
New_Line;
end loop;
end Second_Task;
task Third_Task;
task body Third_Task is
begin
for Index in 1..5 loop
Put("This is in Third_Task, pass number ");
Put(Index, 3);
New_Line;
end loop;
end Third_Task;
begin
Put_Line("This is in the main program.");
end Task1;
-- Result of Execution
-- This is in Third_Task, pass number 1
-- This is in Third_Task, pass number 2
-- This is in Third_Task, pass number 3
-- This is in Third_Task, pass number 4
-- This is in Third_Task, pass number 5
-- This is in Second_Task, pass number 1
-- This is in Second_Task, pass number 2
-- This is in Second_Task, pass number 3
-- This is in Second_Task, pass number 4
-- This is in Second_Task, pass number 5
-- This is in Second_Task, pass number 6
-- This is in Second_Task, pass number 7
-- This is in First Task, pass number 1
-- This is in First Task, pass number 2
-- This is in First Task, pass number 3
-- This is in First Task, pass number 4
-- This is in the main program.
-- Chapter 26 - Program 3
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Task2 is
task First_Task;
task body First_Task is
begin
for Index in 1..4 loop
delay 2.0;
Put("This is in First_Task, pass number ");
Put(Index, 3);
New_Line;
end loop;
end First_Task;
task Second_Task;
task body Second_Task is
begin
for Index in 1..7 loop
delay 1.0;
Put("This is in Second_Task, pass number");
Put(Index, 3);
New_Line;
end loop;
end Second_Task;
task Third_Task;
task body Third_Task is
begin
for Index in 1..5 loop
delay 0.3;
Put("This is in Third_Task, pass number ");
Put(Index, 3);
New_Line;
end loop;
end Third_Task;
begin
-- for Index in 1..5 loop
-- delay 0.7;
Put_Line("This is in the main program.");
-- end loop;
end Task2;
-- Result of Execution (with comments in main program)
-- This is in the main program.
-- This is in Third_Task, pass number 1
-- This is in Third_Task, pass number 2
-- This is in Third_Task, pass number 3
-- This is in Second_Task, pass number 1
-- This is in Third_Task, pass number 4
-- This is in Third_Task, pass number 5
-- This is in First Task, pass number 1
-- This is in Second_Task, pass number 2
-- This is in Second_Task, pass number 3
-- This is in First Task, pass number 2
-- This is in Second_Task, pass number 4
-- This is in Second_Task, pass number 5
-- This is in First Task, pass number 3
-- This is in Second_Task, pass number 6
-- This is in Second_Task, pass number 7
-- This is in First Task, pass number 4
-- Result of Execution (with main program comments removed)
-- This is in Third_Task, pass number 1
-- This is in Third_Task, pass number 2
-- This is in the main program.
-- This is in Third_Task, pass number 3
-- This is in Second_Task, pass number 1
-- This is in Third_Task, pass number 4
-- This is in the main program.
-- This is in Third_Task, pass number 5
-- This is in First Task, pass number 1
-- This is in Second_Task, pass number 2
-- This is in the main program.
-- This is in the main program.
-- This is in Second_Task, pass number 3
-- This is in the main program.
-- This is in First Task, pass number 2
-- This is in Second_Task, pass number 4
-- This is in Second_Task, pass number 5
-- This is in First Task, pass number 3
-- This is in Second_Task, pass number 6
-- This is in Second_Task, pass number 7
-- This is in First Task, pass number 4
-- Chapter 26 - Program 4
with Ada.Text_IO;
use Ada.Text_IO;
procedure Task3 is
begin
Put_Line("This is in the main program.");
declare
task First_Task;
task Second_Task;
task Third_Task;
task body First_Task is
begin
for Index in 1..4 loop
delay 0.0;
Put_Line("This is in First_Task.");
end loop;
end First_Task;
task body Second_Task is
begin
for Index in 1..4 loop
delay 0.0;
Put_Line("This is in Second_Task.");
end loop;
end Second_Task;
task body Third_Task is
begin
for Index in 1..8 loop
delay 0.0;
Put_Line("This is in Third_Task.");
end loop;
end Third_Task;
begin
delay 0.0;
Put_Line("This is in the block body.");
delay 0.0;
Put_Line("This is also in the block body.");
end; -- of declare
Put_Line("This is at the end of the main program.");
end Task3;
-- Result of Execution
-- This is in the main program.
-- This is in First_Task.
-- This is in Second_Task.
-- This is in the block body.
-- This is in Third_Task.
-- This is in First_Task.
-- This is in Second_Task.
-- This is also in the block body.
-- This is in Third_Task.
-- This is in First_Task.
-- This is in Second_Task.
-- This is in Third_Task.
-- This is in First_Task.
-- This is in Second_Task.
-- This is in Third_Task.
-- This is in Third_Task.
-- This is in Third_Task.
-- This is in Third_Task.
-- This is in Third_Task.
-- This is at the end of the main program.
-- Chapter 26 - Programming exercise 1
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Calendar; use Calendar;
procedure CH26_1 is
package Fix_IO is new Ada.Text_IO.Fixed_IO(DAY_DURATION);
use Fix_IO;
Year,Month,Day : INTEGER;
Start,Seconds : DAY_DURATION;
Time_And_Date : TIME;
-- This procedure outputs the time in an Hour:Minute:Second format
-- using a FLOAT type for splitting the time into the various
-- fields. It works fine for simple time logging but lacks the
-- accuracy required for finer time. If finer time is required,
-- it will be necessary to keep the time in the fixed point format,
-- and use lots of type conversions to get the three fields, and
-- the fractional second field if needed. An alternative method
-- would be to use a floating point type with more significant
-- digits to maintain the accuracy, but it would require more time
-- to execute. This is definitely an application for the fixed
-- point data type.
procedure Output_Time(Time_In_Seconds : DAY_DURATION) is
Time : FLOAT;
Hours, Minutes, Seconds : INTEGER;
begin
Time := FLOAT(Time_In_Seconds);
Hours := INTEGER((Time - 30.0 * 60.0) / (60.0 * 60.0));
Time := Time - FLOAT(Hours) * 60.0 * 60.0;
Minutes := INTEGER((Time - 30.0) / 60.0);
Seconds := INTEGER(Time - 0.5) mod 60;
Put(Hours, 3);
Put(":");
Put(Minutes, 2);
Put(":");
Put(Seconds, 2);
end Output_Time;
begin
Time_And_Date := Clock;
Split(Time_And_Date, Year, Month, Day, Seconds);
Output_Time(Seconds);
Put_Line(" Begin 3.14 second delay");
delay 3.14;
Time_And_Date := Clock;
Split(Time_And_Date, Year, Month, Day, Seconds);
Output_Time(Seconds);
Put_Line(" End of 3.14 second delay");
Time_And_Date := Clock;
Split(Time_And_Date, Year, Month, Day, Start); -- get starting time
for Index in 1..9 loop
Put("The date and time are now");
Time_And_Date := Clock;
Split(Time_And_Date,Year,Month,Day,Seconds);
Put(Month,3);
delay 0.2;
Put(Day,3);
delay 0.1;
Put(Year,5);
delay 0.1;
Put(Seconds - Start,8,3,0);
New_Line;
delay 0.6;
end loop;
Put_Line("Begin non-accumulative timing loop here.");
Time_And_Date := Clock;
Split(Time_And_Date, Year, Month, Day, Start); -- get starting time
for Index in 1..9 loop
Time_And_Date := Clock;
Split(Time_And_Date, Year, Month, Day, Seconds);
Put("The elapsed time is");
Put(Seconds - Start, 8, 3, 0);
New_Line;
delay Day_Duration(Index) - (Seconds - Start);
end loop;
Put(" The current time is ");
Time_And_Date := Clock;
Split(Time_And_Date, Year, Month, Day, Seconds);
Output_Time(Seconds);
end CH26_1;
-- Result of Execution
-- 9:54:13 Begin 3.14 second delay
-- 9:54:16 End of 3.14 second delay
-- The date and time are now 7 22 1988 0.000
-- The date and time are now 7 22 1988 1.090
-- The date and time are now 7 22 1988 2.140
-- The date and time are now 7 22 1988 3.180
-- The date and time are now 7 22 1988 4.230
-- The date and time are now 7 22 1988 5.270
-- The date and time are now 7 22 1988 6.320
-- The date and time are now 7 22 1988 7.360
-- The date and time are now 7 22 1988 8.400
-- Begin non-accumulative timing loop here
-- The elapsed time is 0.000
-- The elapsed time is 1.100
-- The elapsed time is 2.030
-- The elapsed time is 3.020
-- The elapsed time is 4.040
-- The elapsed time is 5.030
-- The elapsed time is 6.020
-- The elapsed time is 7.010
-- The elapsed time is 8.000
-- The current time is 9:54:35
-- Chapter 26 - Programming exercise 2
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure CH26_2 is
begin
for Index in 1..4 loop
delay 0.5;
Put_Line("This is in the main program.");
end loop;
declare
task First_Task;
task Second_Task;
task Third_Task;
task body First_Task is
begin
for Index in 1..4 loop
delay 0.0;
Put_Line("This is in First_Task.");
end loop;
end First_Task;
task body Second_Task is
begin
for Index in 1..4 loop
delay 0.0;
Put_Line("This is in Second_Task.");
end loop;
end Second_Task;
task body Third_Task is
begin
for Index in 1..8 loop
delay 0.0;
Put_Line("This is in Third_Task.");
end loop;
end Third_Task;
begin
delay 0.0;
Put_Line("This is in the block body.");
delay 0.0;
Put_Line("This is also in the block body.");
end; -- of declare
for Index in 1..4 loop
delay 0.5;
Put_Line("This is at the end of the main program.");
end loop;
end CH26_2;
-- Result of Execution
-- This is in the main program.
-- This is in the main program.
-- This is in the main program.
-- This is in the main program.
-- This is in First_Task.
-- This is in Second_Task.
-- THis is in the block body.
-- This is in Third_Task.
-- This is in First_Task.
-- This is in Second_Task.
-- This is also in the block body.
-- This is in Third_Task.
-- This is in First_Task.
-- This is in Second_Task.
-- This is in Third_Task.
-- This is in First_Task.
-- This is in Second_Task.
-- This is in Third_Task.
-- This is in Third_Task.
-- This is in Third_Task.
-- This is in Third_Task.
-- This is in Third_Task.
-- This is at the end of the main program.
-- This is at the end of the main program.
-- This is at the end of the main program.
-- This is at the end of the main program.
-- Chapter 27 - Program 1
with Ada.Text_IO;
use Ada.Text_IO;
procedure HotDog is
task Gourmet is
entry Make_A_Hot_Dog;
end Gourmet;
task body Gourmet is
begin
Put_Line("I am ready to make a hot dog for you");
for Index in 1..4 loop
accept Make_A_Hot_Dog do
delay 0.8;
Put("Put hot dog in bun ");
Put_Line("and add mustard");
end Make_A_Hot_Dog;
end loop;
Put_Line("I am out of hot dogs");
end Gourmet;
begin
for Index in 1..4 loop
Gourmet.Make_A_Hot_Dog;
delay 0.1;
Put_Line("Eat the resulting hot dog");
New_Line;
end loop;
Put_Line("I am not hungry any longer");
end HotDog;
-- Result of execution
-- I am ready to make a hot dog for you
-- Put hot dog in bun and add mustard
-- Eat the resulting hot dog
--
-- Put hot dog in bun and add mustard
-- Eat the resulting hot dog
--
-- Put hot dog in bun and add mustard
-- Eat the resulting hot dog
--
-- Put hot dog in bun and add mustard
-- I am out of hot dogs
-- Eat the resulting hot dog
--
-- I am not hungry any longer
-- Chapter 27 - Program 2
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure HotDogs is
task Gourmet is
entry Make_A_Hot_Dog(Serial_Number : INTEGER);
end Gourmet;
procedure Get_Dog(Serial_Number : INTEGER) renames
Gourmet.Make_A_Hot_Dog;
task body Gourmet is
begin
Put_Line("I am ready to make a hot dog for you");
accept Make_A_Hot_Dog(Serial_Number : INTEGER) do
Put_Line("This will be the first hot dog");
Put("Put hot dog in bun ");
Put_Line("and add mustard");
delay 0.8;
end Make_A_Hot_Dog;
for Index in 1..4 loop
accept Make_A_Hot_Dog(Serial_Number : INTEGER) do
Put("This will be hot dog number");
Put(Serial_Number, 3);
New_Line;
Put("Put hot dog in bun ");
Put_Line("and add mustard");
delay 0.8;
end Make_A_Hot_Dog;
end loop;
accept Make_A_Hot_Dog(Serial_Number : INTEGER) do
Put_Line("This will be the last hot dog");
Put("Put hot dog in bun ");
Put_Line("and add mustard");
delay 0.8;
end Make_A_Hot_Dog;
Put_Line("I am out of hot dogs");
end Gourmet;
begin
for Index in 1..6 loop
Get_Dog(Index);
Put_Line("Eat the resulting hot dog");
New_Line;
end loop;
Put_Line("I am not hungry any longer");
end HotDogs;
-- Result of execution
-- I am ready to make a hot dog for you
-- This will be the first hot dog
-- Put hot dog in bun and add mustard
-- Eat the resulting hot dog
--
-- This will be hot dog number 2
-- Put hot dog in bun and add mustard
-- Eat the resulting hot dog
--
-- This will be hot dog number 3
-- Put hot dog in bun and add mustard
-- Eat the resulting hot dog
--
-- This will be hot dog number 4
-- Put hot dog in bun and add mustard
-- I am out of hot dogs
-- Eat the resulting hot dog
--
-- This will be hot dog number 5
-- Put hot dog in bun and add mustard
-- Eat the resulting hot dog
--
-- This will be the last hot dog
-- Put hot dog in bun and add mustard
-- Eat the resulting hot dog
--
-- I am not hungry any longer
-- Chapter 27 - Program 3
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure ManyDogs is
task Gourmet is
entry Make_A_Hot_Dog(Serial_Number : INTEGER;
With_Mustard : BOOLEAN);
end Gourmet;
task body Gourmet is
begin
Put_Line("I am ready to make a hot dog for you");
for Index in 1..5 loop
accept Make_A_Hot_Dog(Serial_Number : INTEGER;
With_Mustard : BOOLEAN) do
Put("Put hot dog number");
Put(Serial_Number, 2);
Put(" in bun ");
if With_Mustard then
Put_Line("and add mustard");
else
Put_Line("and hold the mustard");
end if;
delay 0.8;
end Make_A_Hot_Dog;
end loop;
Put_Line("I am out of hot dogs");
end Gourmet;
task Bill;
task John;
task body Bill is
begin
for Index in 1..3 loop
Gourmet.Make_A_Hot_Dog(Index, FALSE);
Put_Line("Bill is eating the hot dog without mustard");
New_Line;
end loop;
Put_Line("Bill is not hungry any longer");
end Bill;
task body John is
begin
for Index in 1..2 loop
Gourmet.Make_A_Hot_Dog(Index, TRUE);
Put_Line("John is eating the hot dog with mustard");
New_Line;
end loop;
Put_Line("John is not hungry any longer");
end John;
begin
null;
end ManyDogs;
-- Result of execution
-- I am ready to make a hot dog for you
-- Put hot dog number 1 in bun and add mustard
-- Put hot dog number 1 in bun and hold the mustard
-- John is eating the hot dog with mustard
--
-- Put hot dog number 2 in bun and add mustard
-- Bill is eating the hot dog without mustard
--
-- Put hot dog number 2 in bun and hold the mustard
-- John is eating the hot dog with mustard
--
-- John is not hungry any longer
-- Bill is eating the hot dog without mustard
--
-- Put hot dog number 3 in bun and hold the mustard
-- I am out of hot dogs
-- Bill is eating the hot dog without mustard
--
-- Bill is not hungry any longer
-- Chapter 27 - Program 4
with Ada.Text_IO;
use Ada.Text_IO;
procedure Retail1 is
Number_Of_Dogs : INTEGER := 0;
task Retail_Hot_Dogs is
entry Stock_With_A_Hot_Dog;
entry Deliver_A_Hot_Dog;
end Retail_Hot_Dogs;
task body Retail_Hot_Dogs is
begin
accept Stock_With_A_Hot_Dog do
Put_Line("Put the first hot dog on the shelf");
Number_Of_Dogs := Number_Of_Dogs + 1;
end Stock_With_A_Hot_Dog;
for Index in 1..7 loop
Put("In loop => ");
select
accept Stock_With_A_Hot_Dog do
Put_Line("Add a hot dog to the shelf");
Number_Of_Dogs := Number_Of_Dogs + 1;
end Stock_With_A_Hot_Dog;
or
accept Deliver_A_Hot_Dog do
Put_Line("Remove a hot dog from the shelf");
Number_Of_Dogs := Number_Of_Dogs - 1;
end Deliver_A_Hot_Dog;
end select;
end loop;
end Retail_Hot_Dogs;
begin
for Index in 1..4 loop
Retail_Hot_Dogs.Stock_With_A_Hot_Dog;
Retail_Hot_Dogs.Deliver_A_Hot_Dog;
end loop;
end Retail1;
-- Result of execution
-- Put the first hot dog on the shelf
-- In loop => Remove a hot dog from the shelf
-- In loop => Add a hot dog to the shelf
-- In loop => Remove a hot dog from the shelf
-- In loop => Add a hot dog to the shelf
-- In loop => Remove a hot dog from the shelf
-- In loop => Add a hot dog to the shelf
-- In loop => Remove a hot dog from the shelf
-- Chapter 27 - Program 5
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Retail2 is
Number_Of_Dogs : INTEGER := 0;
-- The main program adds and deletes
-- hot dogs to/from the shelf.
task Five_Dogs; -- This task adds five hot dogs to stock
task Remove_Five_Dogs; -- This task deletes five from stock
task Retail_Hot_Dogs is
entry Stock_With_A_Hot_Dog; -- This adds a hot dog to stock
entry Deliver_A_Hot_Dog; -- This deletes one from stock
end Retail_Hot_Dogs;
task body Retail_Hot_Dogs is
begin
for Index in 1..18 loop
Put("In loop => ");
select
when Number_Of_Dogs < 8 =>
accept Stock_With_A_Hot_Dog do
Number_Of_Dogs := Number_Of_Dogs + 1;
Put("Add a hot dog to the shelf, number =");
Put(Number_Of_Dogs, 3);
New_Line;
end Stock_With_A_Hot_Dog;
or
when Number_Of_Dogs > 0 =>
accept Deliver_A_Hot_Dog do
Put_Line("Remove a hot dog from the shelf");
Number_Of_Dogs := Number_Of_Dogs - 1;
end Deliver_A_Hot_Dog;
end select;
end loop;
end Retail_Hot_Dogs;
task body Five_Dogs is
begin
for Index in 1..5 loop
delay 0.1;
Retail_Hot_Dogs.Stock_With_A_Hot_Dog;
end loop;
end Five_Dogs;
task body Remove_Five_Dogs is
begin
for Index in 1..5 loop
delay 0.6;
Retail_Hot_Dogs.Deliver_A_Hot_Dog;
end loop;
end Remove_Five_Dogs;
begin
for Index in 1..4 loop
delay 0.9;
Retail_Hot_Dogs.Stock_With_A_Hot_Dog;
Retail_Hot_Dogs.Deliver_A_Hot_Dog;
end loop;
end Retail2;
-- Result of execution (With no changes)
-- Add a hot dog to the shelf, number = 1
-- Add a hot dog to the shelf, number = 2
-- Add a hot dog to the shelf, number = 3
-- Add a hot dog to the shelf, number = 4
-- Add a hot dog to the shelf, number = 5
-- Remove a hot dog from the shelf
-- Add a hot dog to the shelf, number = 5
-- Remove a hot dog from the shelf
-- Remove a hot dog from the shelf
-- Remove a hot dog from the shelf
-- Add a hot dog to the shelf, number = 3
-- Remove a hot dog from the shelf
-- Remove a hot dog from the shelf
-- Add a hot dog to the shelf, number = 2
-- Remove a hot dog from the shelf
-- Remove a hot dog from the shelf
-- Add a hot dog to the shelf, number = 1
-- Remove a hot dog from the shelf
-- Result of execution (With line 29 changed so limit is 3)
-- Add a hot dog to the shelf, number = 1
-- Add a hot dog to the shelf, number = 2
-- Add a hot dog to the shelf, number = 3
-- Remove a hot dog from the shelf
-- Add a hot dog to the shelf, number = 3
-- Remove a hot dog from the shelf
-- Add a hot dog to the shelf, number = 3
-- Remove a hot dog from the shelf
-- Add a hot dog to the shelf, number = 3
-- Remove a hot dog from the shelf
-- Add a hot dog to the shelf, number = 3
-- Remove a hot dog from the shelf
-- Remove a hot dog from the shelf
-- Add a hot dog to the shelf, number = 2
-- Remove a hot dog from the shelf
-- Remove a hot dog from the shelf
-- Add a hot dog to the shelf, number = 1
-- Remove a hot dog from the shelf
-- Result of execution (With delays swapped in lines 49 and 57)
-- Add a hot dog to the shelf, number = 1
-- Remove a hot dog from the shelf
-- Add a hot dog to the shelf, number = 1
-- Remove a hot dog from the shelf
-- Add a hot dog to the shelf, number = 1
-- Remove a hot dog from the shelf
-- Add a hot dog to the shelf, number = 1
-- Remove a hot dog from the shelf
-- Add a hot dog to the shelf, number = 1
-- Remove a hot dog from the shelf
-- Add a hot dog to the shelf, number = 1
-- Remove a hot dog from the shelf
-- Add a hot dog to the shelf, number = 1
-- Remove a hot dog from the shelf
-- Add a hot dog to the shelf, number = 1
-- Remove a hot dog from the shelf
-- Add a hot dog to the shelf, number = 1
-- Remove a hot dog from the shelf
-- Chapter 27 - Program 6
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Protect is

protected Animals is
procedure Add_Some(Dogs_In : INTEGER; Cats_IN : INTEGER);
procedure Subtract_Some(Dogs_In : INTEGER; Cats_In : INTEGER);
procedure Get_Count(Dogs_Out : out INTEGER;
Cats_Out : out INTEGER);
private
Dogs : INTEGER := 5;
Cats : INTEGER := 3;
end Animals;
protected body Animals is

procedure Add_Some(Dogs_In : INTEGER; Cats_In : INTEGER) is
begin
Dogs := Dogs + Dogs_In;
Cats := Cats + Cats_In;
end Add_Some;
procedure Subtract_Some(Dogs_In : INTEGER; Cats_In : INTEGER) is
begin
Dogs := Dogs - Dogs_In;
Cats := Cats - Cats_In;
end Subtract_Some;
procedure Get_Count(Dogs_Out : out INTEGER;
Cats_Out : out INTEGER) is
begin
Dogs_Out := Dogs;
Cats_Out := Cats;
end Get_Count;
end Animals;
task Farm_Animals;
task City_Animals;
task Catch_Animals;
task body Farm_Animals is
begin
for Index in 1..5 loop
delay(0.3);
Animals.Subtract_Some(3, 2);
end loop;
end Farm_Animals;
task body City_Animals is
begin
for Index in 1..4 loop
delay(0.5);
Animals.Subtract_Some(1, 2);
end loop;
end City_Animals;
task body Catch_Animals is
begin
for Index in 1..7 loop
delay(0.6);
Animals.Add_Some(7, 4);
end loop;
end Catch_Animals;
Number_Of_Dogs : INTEGER;
Number_Of_Cats : INTEGER;
begin
for Index in 1..12 loop
Animals.Get_Count(Number_Of_Dogs, Number_Of_Cats);
Put("Dog count = ");
Put(Number_Of_Dogs, 4);
Put(" Cat count = ");
Put(Number_Of_Cats, 4);
New_Line;
delay 0.5;
end loop;
end Protect;
-- Result of execution
-- Dog count = 5 Cat count = 3
-- Dog count = 1 Cat count = -1
-- Dog count = 1 Cat count = -3
-- Dog count = 4 Cat count = -3
-- Dog count = 7 Cat count = -3
-- Dog count = 14 Cat count = 1
-- Dog count = 21 Cat count = 5
-- Dog count = 21 Cat count = 5
-- Dog count = 28 Cat count = 9
-- Dog count = 35 Cat count = 13
-- Dog count = 35 Cat count = 13
-- Dog count = 35 Cat count = 13
-- Chapter 27 - Programming exercise 1
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure CH27_1 is
task Gourmet is
entry Make_A_Hot_Dog;
end Gourmet;
task body Gourmet is
begin
Put_Line("I am ready to make a hot dog for you");
for Index in 1..4 loop
accept Make_A_Hot_Dog do
null;
end Make_A_Hot_Dog;
delay 0.8;
Put("Put hot dog in bun ");
Put_Line("and add mustard");
end loop;
Put_Line("I am out of hot dogs");
end Gourmet;
begin
for Index in 1..4 loop
Gourmet.Make_A_Hot_Dog;
delay 0.1;
Put_Line("Eat the resulting hot dog");
New_Line;
end loop;
Put_Line("I am not hungry any longer");
end CH27_1;
-- Result of execution
-- I am ready to make a hot dog for you
-- Eat the resulting hot dog
--
-- Put hot dog in bun and add mustard
-- Eat the resulting hot dog
--
-- Put hot dog in bun and add mustard
-- Eat the resulting hot dog
--
-- Put hot dog in bun and add mustard
-- Eat the resulting hot dog
--
-- I am not hungry any longer
-- Put hot dog in bun and add mustard
-- I am out of hot dogs
-- Chapter 27 - Programming exercise 2
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure CH27_2 is
Number_Of_Dogs : INTEGER := 0;
-- The main program adds and deletes
-- hot dogs to/from the shelf.
task Five_Dogs; -- This task adds five hot dogs to stock
task Remove_Five_Dogs; -- This task deletes five from stock
task How_Many_Are_Left; -- This task lists the number on shelf
task Retail_Hot_Dogs is
entry Stock_With_A_Hot_Dog; -- This adds a hot dog to stock
entry Deliver_A_Hot_Dog; -- This deletes one from stock
end Retail_Hot_Dogs;
task body Retail_Hot_Dogs is
begin
for Index in 1..18 loop
select
when Number_Of_Dogs < 8 =>
accept Stock_With_A_Hot_Dog do
Number_Of_Dogs := Number_Of_Dogs + 1;
Put("Add a hot dog to the shelf, number =");
Put(Number_Of_Dogs,3);
New_Line;
end Stock_With_A_Hot_Dog;
or
when Number_Of_Dogs > 0 =>
accept Deliver_A_Hot_Dog do
Put_Line("Remove a hot dog from the shelf");
Number_Of_Dogs := Number_Of_Dogs - 1;
end Deliver_A_Hot_Dog;
end select;
end loop;
end Retail_Hot_Dogs;
task body Five_Dogs is
begin
for Index in 1..5 loop
delay 0.1;
Retail_Hot_Dogs.Stock_With_A_Hot_Dog;
end loop;
end Five_Dogs;
task body Remove_Five_Dogs is
begin
for Index in 1..5 loop
delay 0.6;
Retail_Hot_Dogs.Deliver_A_Hot_Dog;
end loop;
end Remove_Five_Dogs;
task body How_Many_Are_Left is
begin
for Index in 1..10 loop
delay 0.3;
Put("There are");
Put(Number_Of_Dogs,3);
Put_Line(" hotdogs left on the shelf.");
end loop;
end How_Many_Are_Left;
begin
for Index in 1..4 loop
delay 0.9;
Retail_Hot_Dogs.Stock_With_A_Hot_Dog;
Retail_Hot_Dogs.Deliver_A_Hot_Dog;
end loop;
end CH27_2;
-- Result of execution (With no changes)
-- Add a hot dog to the shelf, number = 1
-- Add a hot dog to the shelf, number = 2
-- Add a hot dog to the shelf, number = 3
-- There are 3 hotdogs left on the shelf.
-- Add a hot dog to the shelf, number = 4
-- Add a hot dog to the shelf, number = 5
-- Remove a hot dog from the shelf
-- There are 4 hotdogs left on the shelf.
-- Add a hot dog to the shelf, number = 5
-- Remove a hot dog from the shelf
-- There are 4 hotdogs left on the shelf.
-- Remove a hot dog from the shelf
-- There are 3 hotdogs left on the shelf.
-- There are 3 hotdogs left on the shelf.
-- Remove a hot dog from the shelf
-- Add a hot dog to the shelf, number = 3
-- There are 3 hotdogs left on the shelf.
-- Remove a hot dog from the shelf
-- There are 2 hotdogs left on the shelf.
-- Remove a hot dog from the shelf
-- There are 1 hotdogs left on the shelf.
-- There are 1 hotdogs left on the shelf.
-- Add a hot dog to the shelf, number = 2
-- Remove a hot dog from the shelf
-- Remove a hot dog from the shelf
-- There are 0 hotdogs left on the shelf.
-- Add a hot dog to the shelf, number = 1
-- Remove a hot dog from the shelf
-- Chapter 27 - Programming exercise 3
with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Calendar;
use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Calendar;
procedure CH27_3 is
package Fix_IO is new Ada.Text_IO.Fixed_IO(DAY_DURATION);
use Fix_IO;
Number_Of_Dogs : INTEGER := 0;
-- The main program adds and deletes
-- hot dogs to/from the shelf.
task Five_Dogs; -- This task adds five hot dogs to stock
task Remove_Five_Dogs; -- This task deletes five from stock
task How_Many_Are_Left; -- This task lists the number on shelf
task Retail_Hot_Dogs is
entry Stock_With_A_Hot_Dog; -- This adds a hot dog to stock
entry Deliver_A_Hot_Dog; -- This deletes one from stock
end Retail_Hot_Dogs;
task body Retail_Hot_Dogs is
begin
for Index in 1..18 loop
select
when Number_Of_Dogs < 8 =>
accept Stock_With_A_Hot_Dog do
Number_Of_Dogs := Number_Of_Dogs + 1;
Put("Add a hot dog to the shelf, number =");
Put(Number_Of_Dogs,3);
New_Line;
end Stock_With_A_Hot_Dog;
or
when Number_Of_Dogs > 0 =>
accept Deliver_A_Hot_Dog do
Put_Line("Remove a hot dog from the shelf");
Number_Of_Dogs := Number_Of_Dogs - 1;
end Deliver_A_Hot_Dog;
end select;
end loop;
end Retail_Hot_Dogs;
task body Five_Dogs is
begin
for Index in 1..5 loop
delay 0.1;
Retail_Hot_Dogs.Stock_With_A_Hot_Dog;
end loop;
end Five_Dogs;
task body Remove_Five_Dogs is
begin
for Index in 1..5 loop
delay 0.6;
Retail_Hot_Dogs.Deliver_A_Hot_Dog;
end loop;
end Remove_Five_Dogs;
task body How_Many_Are_Left is
Time_And_Date : TIME;
Start, Seconds : DAY_DURATION;
Year, Month, Day : INTEGER;
begin
Time_And_Date := Clock;
Split(Time_And_Date, Year, Month, Day, Start);
for Index in 1..10 loop
delay 0.3;
Time_And_Date := Clock;
Split(Time_And_Date, Year, Month, Day, Seconds);
Put("Elapsed time =");
Put(Seconds - Start, 3, 3, 0);
Put(", and there are");
Put(Number_Of_Dogs, 3);
Put_Line(" hotdogs left on the shelf.");
end loop;
end How_Many_Are_Left;
begin
for Index in 1..4 loop
delay 0.9;
Retail_Hot_Dogs.Stock_With_A_Hot_Dog;
Retail_Hot_Dogs.Deliver_A_Hot_Dog;
end loop;
end CH27_3;
-- Result of execution (With no changes)
-- Add a hot dog to the shelf, number = 1
-- Add a hot dog to the shelf, number = 2
-- Add a hot dog to the shelf, number = 3
-- Elapsed time = 0.330, and there are 3 hotdogs left on the shelf.
-- Add a hot dog to the shelf, number = 4
-- Add a hot dog to the shelf, number = 5
-- Remove a hot dog from the shelf
-- Elapsed time = 0.660, and there are 4 hotdogs left on the shelf.
-- Add a hot dog to the shelf, number = 5
-- Remove a hot dog from the shelf
-- Elapsed time = 0.990, and there are 4 hotdogs left on the shelf.
-- Remove a hot dog from the shelf
-- Elapsed time = 1.320, and there are 3 hotdogs left on the shelf.
-- Elapsed time = 1.650, and there are 3 hotdogs left on the shelf.
-- Remove a hot dog from the shelf
-- Add a hot dog to the shelf, number = 3
-- Elapsed time = 1.980, and there are 3 hotdogs left on the shelf.
-- Remove a hot dog from the shelf
-- Elapsed time = 2.290, and there are 2 hotdogs left on the shelf.
-- Remove a hot dog from the shelf
-- Elapsed time = 2.620, and there are 1 hotdogs left on the shelf.
-- Elapsed time = 2.950, and there are 1 hotdogs left on the shelf.
-- Add a hot dog to the shelf, number = 2
-- Remove a hot dog from the shelf
-- Remove a hot dog from the shelf
-- Elapsed time = 3.280, and there are 0 hotdogs left on the shelf.
-- Add a hot dog to the shelf, number = 1
-- Remove a hot dog from the shelf
-- Chapter 28 - Program 1
with Ada.Text_IO;
use Ada.Text_IO;
procedure Meals1 is
HOURS : constant := 1;
type PERSON is (BILL, JOHN);
package Enum_IO is new Ada.Text_IO.Enumeration_IO(PERSON);
use Enum_IO;
task Bills_Day;
task Johns_Day;
task Restaurant is
entry Eat_A_Meal(Customer : PERSON);
end Restaurant;
task Burger_Boy is
entry Eat_A_Meal(Customer : PERSON);
end Burger_Boy;
task body Bills_Day is
My_Name : PERSON := BILL;
begin
delay 1.0 * HOURS;
Restaurant.Eat_A_Meal(My_Name);
delay 1.0 * HOURS;
Restaurant.Eat_A_Meal(My_Name);
delay 1.0 * HOURS;
Restaurant.Eat_A_Meal(My_Name);
end Bills_Day;
task body Johns_Day is
My_Name : PERSON := JOHN;
begin
delay 0.4 * HOURS;
Restaurant.Eat_A_Meal(My_Name);
delay 0.4 * HOURS;
Restaurant.Eat_A_Meal(My_Name);
delay 4.0 * HOURS;
Restaurant.Eat_A_Meal(My_Name);
end Johns_Day;
task body Restaurant is
begin
loop
accept Eat_A_Meal(Customer : PERSON) do
Put(Customer);
Put_Line(" is ordering at the restaurant");
delay 0.5 * HOURS;
Put(Customer);
Put_Line(" is eating at the restaurant");
delay 0.5 * HOURS;
end Eat_A_Meal;
end loop;
end Restaurant;
task body Burger_Boy is
begin
loop
accept Eat_A_Meal(Customer : PERSON) do
Put(Customer);
Put_Line(" is ordering at the Burger Boy");
delay 0.1 * HOURS;
Put(Customer);
Put_Line(" is eating at the Burger Boy");
delay 0.1 * HOURS;
end Eat_A_Meal;
end loop;
end Burger_Boy;
begin
null;
end Meals1;
-- Result of execution
-- JOHN is ordering at the restaurant
-- JOHN is eating at the restaurant
-- BILL is ordering at the restaurant
-- BILL is eating at the restaurant
-- JOHN is ordering at the restaurant
-- JOHN is eating at the restaurant
-- BILL is ordering at the restaurant
-- BILL is eating at the restaurant
-- BILL is ordering at the restaurant
-- BILL is eating at the restaurant
-- JOHN is ordering at the restaurant
-- JOHN is eating at the restaurant
--
-- (The program will halt due to deadlock.)
-- Chapter 28 - Program 2
with Ada.Text_IO;
use Ada.Text_IO;
procedure Meals2 is
HOURS : constant := 1;
type PERSON is (BILL, JOHN);
package Enum_IO is new Ada.Text_IO.Enumeration_IO(PERSON);
use Enum_IO;
task Bills_Day;
task Johns_Day;
task Restaurant is
entry Eat_A_Meal(Customer : PERSON);
end Restaurant;
task Burger_Boy is
entry Eat_A_Meal(Customer : PERSON);
end Burger_Boy;
task body Bills_Day is
My_Name : PERSON := BILL;
begin
delay 1.0 * HOURS;
select
Restaurant.Eat_A_Meal(My_Name);
else
Burger_Boy.Eat_A_Meal(My_Name);
end select;
delay 1.0 * HOURS;
select
Restaurant.Eat_A_Meal(My_Name);
or
delay 0.1 * HOURS;
Burger_Boy.Eat_A_Meal(My_Name);
end select;
delay 1.0 * HOURS;
Restaurant.Eat_A_Meal(My_Name);
end Bills_Day;
task body Johns_Day is
My_Name : PERSON := JOHN;
begin
delay 0.4 * HOURS;
Restaurant.Eat_A_Meal(My_Name);
delay 0.4 * HOURS;
Restaurant.Eat_A_Meal(My_Name);
delay 4.0 * HOURS;
Restaurant.Eat_A_Meal(My_Name);
end Johns_Day;
task body Restaurant is
begin
loop
accept Eat_A_Meal(Customer : PERSON) do
Put(Customer);
Put_Line(" is ordering at the restaurant");
delay 0.5 * HOURS;
Put(Customer);
Put_Line(" is eating at the restaurant");
delay 0.5 * HOURS;
end Eat_A_Meal;
end loop;
end Restaurant;
task body Burger_Boy is
begin
loop
accept Eat_A_Meal(Customer : PERSON) do
Put(Customer);
Put_Line(" is ordering at the Burger Boy");
delay 0.1 * HOURS;
Put(Customer);
Put_Line(" is eating at the Burger Boy");
delay 0.1 * HOURS;
end Eat_A_Meal;
end loop;
end Burger_Boy;
begin
null;
end Meals2;
-- Result of execution
-- JOHN is ordering at the restaurant
-- JOHN is eating at the restaurant
-- BILL is ordering at the Burger Boy
-- BILL is eating at the Burger Boy
-- JOHN is ordering at the restaurant
-- BILL is ordering at the Burger Boy
-- BILL is eating at the Burger Boy
-- JOHN is eating at the restaurant
-- BILL is ordering at the restaurant
-- BILL is eating at the restaurant
-- JOHN is ordering at the restaurant
-- JOHN is eating at the restaurant
--
-- (The program will halt due to deadlock.)
-- Chapter 28 - Program 3
with Ada.Text_IO;
use Ada.Text_IO;
procedure Meals3 is
HOURS : constant := 1;
type PERSON is (BILL, JOHN);
package Enum_IO is new Ada.Text_IO.Enumeration_IO(PERSON);
use Enum_IO;
task Bills_Day;
task Johns_Day;
task Restaurant is
entry Eat_A_Meal(Customer : PERSON);
end Restaurant;
task Burger_Boy is
entry Eat_A_Meal(Customer : PERSON);
end Burger_Boy;
task body Bills_Day is
My_Name : PERSON := BILL;
begin
delay 1.0 * HOURS;
select
Restaurant.Eat_A_Meal(My_Name);
else
Burger_Boy.Eat_A_Meal(My_Name);
end select;
delay 1.0 * HOURS;
select
Restaurant.Eat_A_Meal(My_Name);
or
delay 0.1 * HOURS;
Burger_Boy.Eat_A_Meal(My_Name);
end select;
delay 1.0 * HOURS;
Restaurant.Eat_A_Meal(My_Name);
end Bills_Day;
task body Johns_Day is
My_Name : PERSON := JOHN;
begin
delay 0.4 * HOURS;
Restaurant.Eat_A_Meal(My_Name);
delay 0.4 * HOURS;
Restaurant.Eat_A_Meal(My_Name);
delay 4.0 * HOURS;
Restaurant.Eat_A_Meal(My_Name);
end Johns_Day;
task body Restaurant is
begin
loop
select
accept Eat_A_Meal(Customer : PERSON) do
Put(Customer);
Put_Line(" is ordering at the restaurant");
delay 0.5 * HOURS;
Put(Customer);
Put_Line(" is eating at the restaurant");
delay 0.5 * HOURS;
end Eat_A_Meal;
or
delay 1.5 * HOURS;
Put_Line("The restaurant is closed for the day");
exit;
end select;
end loop;
end Restaurant;
task body Burger_Boy is
begin
loop
select
accept Eat_A_Meal(Customer : PERSON) do
Put(Customer);
Put_Line(" is ordering at the Burger Boy");
delay 0.1 * HOURS;
Put(Customer);
Put_Line(" is eating at the Burger Boy");
delay 0.1 * HOURS;
end Eat_A_Meal;
or
delay 2.1 * HOURS;
Put_Line("The Burger Boy is closed for the day");
exit;
end select;
end loop;
end Burger_Boy;
begin
null;
end Meals3;
-- Result of execution
-- JOHN is ordering at the restaurant
-- JOHN is eating at the restaurant
-- BILL is ordering at the Burger Boy
-- BILL is eating at the Burger Boy
-- JOHN is ordering at the restaurant
-- BILL is ordering at the Burger Boy
-- BILL is eating at the Burger Boy
-- JOHN is eating at the restaurant
-- BILL is ordering at the restaurant
-- BILL is eating at the restaurant
-- The Burger Boy is closed for the day
-- The restaurant is closed for the day
-- Chapter 28 - Program 4
with Ada.Text_IO;
use Ada.Text_IO;
procedure Terminat is
HOURS : constant := 1;
type PERSON is (BILL, JOHN);
package Enum_IO is new Ada.Text_IO.Enumeration_IO(PERSON);
use Enum_IO;
task Bills_Day;
task Johns_Day;
task Restaurant is
entry Eat_A_Meal(Customer : PERSON);
end Restaurant;
task Burger_Boy is
entry Eat_A_Meal(Customer : PERSON);
end Burger_Boy;
task body Bills_Day is
My_Name : PERSON := BILL;
begin
delay 1.0 * HOURS;
Restaurant.Eat_A_Meal(My_Name);
delay 1.0 * HOURS;
Restaurant.Eat_A_Meal(My_Name);
delay 1.0 * HOURS;
Restaurant.Eat_A_Meal(My_Name);
end Bills_Day;
task body Johns_Day is
My_Name : PERSON := JOHN;
begin
delay 0.4 * HOURS;
Restaurant.Eat_A_Meal(My_Name);
delay 0.4 * HOURS;
Restaurant.Eat_A_Meal(My_Name);
delay 4.0 * HOURS;
Restaurant.Eat_A_Meal(My_Name);
end Johns_Day;
task body Restaurant is
begin
loop
select
accept Eat_A_Meal(Customer : PERSON) do
Put(Customer);
Put_Line(" is ordering at the restaurant");
delay 0.5 * HOURS;
Put(Customer);
Put_Line(" is eating at the restaurant");
delay 0.5 * HOURS;
end Eat_A_Meal;
or
terminate;
end select;
end loop;
end Restaurant;
task body Burger_Boy is
begin
loop
select
accept Eat_A_Meal(Customer : PERSON) do
Put(Customer);
Put_Line(" is ordering at the Burger Boy");
delay 0.1 * HOURS;
Put(Customer);
Put_Line(" is eating at the Burger Boy");
delay 0.1 * HOURS;
end Eat_A_Meal;
or
terminate;
end select;
end loop;
end Burger_Boy;
begin
null;
end Terminat;
-- Result of execution
-- JOHN is ordering at the restaurant
-- JOHN is eating at the restaurant
-- BILL is ordering at the restaurant
-- Bill is eating at the restaurant
-- JOHN is ordering at the restaurant
-- JOHN is eating at the restaurant
-- BILL is ordering at the restaurant
-- BILL is eating at the restaurant
-- BILL is ordering at the restaurant
-- BILL is eating at the restaurant
-- JOHN is ordering at the restaurant
-- JOHN is eating at the restaurant
-- Chapter 28 - Programming exercise 1
with Ada.Text_IO;
use Ada.Text_IO;
procedure CH28_1 is
HOURS : constant := 1;
type PERSON is (BILL, JOHN);
package Enum_IO is new Ada.Text_IO.Enumeration_IO(PERSON);
use Enum_IO;
task Bills_Day;
task Johns_Day;
task Restaurant is
entry Eat_A_Meal(Customer : PERSON);
end Restaurant;
task Burger_Boy is
entry Eat_A_Meal(Customer : PERSON);
end Burger_Boy;
task body Bills_Day is
My_Name : PERSON := BILL;
begin
delay 1.0 * HOURS;
select
Restaurant.Eat_A_Meal(My_Name);
else
Burger_Boy.Eat_A_Meal(My_Name);
end select;
delay 1.0 * HOURS;
select
Restaurant.Eat_A_Meal(My_Name);
or
delay 0.1 * HOURS;
Burger_Boy.Eat_A_Meal(My_Name);
end select;
delay 1.0 * HOURS;
Restaurant.Eat_A_Meal(My_Name);
end Bills_Day;
task body Johns_Day is
My_Name : PERSON := JOHN;
begin
delay 0.4 * HOURS;
Restaurant.Eat_A_Meal(My_Name);
delay 0.4 * HOURS;
Restaurant.Eat_A_Meal(My_Name);
delay 4.0 * HOURS;
Restaurant.Eat_A_Meal(My_Name);
end Johns_Day;
task body Restaurant is
begin
loop
select
accept Eat_A_Meal(Customer : PERSON) do
Put(Customer);
Put_Line(" is ordering at the restaurant");
delay 0.5 * HOURS;
Put(Customer);
Put_Line(" is eating at the restaurant");
delay 0.5 * HOURS;
end Eat_A_Meal;
or
terminate;
end select;
end loop;
end Restaurant;
task body Burger_Boy is
begin
loop
select
accept Eat_A_Meal(Customer : PERSON) do
Put(Customer);
Put_Line(" is ordering at the Burger Boy");
delay 0.1 * HOURS;
Put(Customer);
Put_Line(" is eating at the Burger Boy");
delay 0.1 * HOURS;
end Eat_A_Meal;
or
terminate;
end select;
end loop;
end Burger_Boy;
begin
null;
end CH28_1;
-- Result of execution
-- JOHN is ordering at the restaurant
-- JOHN is eating at the restaurant
-- BILL is ordering at the Burger Boy
-- BILL is eating at the Burger Boy
-- JOHN is ordering at the restaurant
-- BILL is ordering at the Burger Boy
-- BILL is eating at the Burger Boy
-- JOHN is eating at the restaurant
-- BILL is ordering at the restaurant
-- BILL is eating at the restaurant
-- JOHN is ordering at the restaurant
-- JOHN is eating at the restaurant
-- Chapter 28 - Programming exercise 2
with Ada.Text_IO;
use Ada.Text_IO;
procedure CH28_2 is
HOURS : constant := 1;
type PERSON is (BILL, JOHN);
package Enum_IO is new Ada.Text_IO.Enumeration_IO(PERSON);
use Enum_IO;
task Bills_Day;
task Johns_Day;
task Restaurant is
entry Eat_A_Meal(Customer : PERSON);
end Restaurant;
task Burger_Boy is
entry Eat_A_Meal(Customer : PERSON);
end Burger_Boy;
task body Bills_Day is
My_Name : PERSON := BILL;
begin
delay 1.0 * HOURS;
select
Restaurant.Eat_A_Meal(My_Name);
else
Burger_Boy.Eat_A_Meal(My_Name);
end select;
delay 1.0 * HOURS;
select
Restaurant.Eat_A_Meal(My_Name);
or
delay 0.1 * HOURS;
Burger_Boy.Eat_A_Meal(My_Name);
end select;
delay 1.0 * HOURS;
Restaurant.Eat_A_Meal(My_Name);
end Bills_Day;
procedure Johns_Choice(Name : PERSON) is
begin
select
Burger_Boy.Eat_A_Meal(Name);
else
Restaurant.Eat_A_Meal(Name);
end select;
end;
task body Johns_Day is
My_Name : PERSON := JOHN;
begin
delay 0.4 * HOURS;
Johns_Choice(My_Name);
delay 0.4 * HOURS;
Johns_Choice(My_Name);
delay 4.0 * HOURS;
Johns_Choice(My_Name);
end Johns_Day;
task body Restaurant is
begin
loop
select
accept Eat_A_Meal(Customer : PERSON) do
Put(Customer);
Put_Line(" is ordering at the restaurant");
delay 0.5 * HOURS;
Put(Customer);
Put_Line(" is eating at the restaurant");
delay 0.5 * HOURS;
end Eat_A_Meal;
or
terminate;
end select;
end loop;
end Restaurant;
task body Burger_Boy is
begin
loop
select
accept Eat_A_Meal(Customer : PERSON) do
Put(Customer);
Put_Line(" is ordering at the Burger Boy");
delay 0.1 * HOURS;
Put(Customer);
Put_Line(" is eating at the Burger Boy");
delay 0.1 * HOURS;
end Eat_A_Meal;
or
terminate;
end select;
end loop;
end Burger_Boy;
begin
null;
end CH28_2;
-- Result of execution
-- JOHN is ordering at the Burger Boy
-- JOHN is eating at the Burger Boy
-- BILL is ordering at the restaurant
-- JOHN is ordering at the Burger Boy
-- JOHN is eating at the Burger Boy
-- BILL is eating at the restaurant
-- BILL is ordering at the restaurant
-- BILL is eating at the restaurant
-- BILL is ordering at the restaurant
-- JOHN is ordering at the Burger Boy
-- JOHN is eating at the Burger Boy
-- BILL is eating at the restaurant
-- Chapter 29 - Program 1
with Ada.Text_IO;
use Ada.Text_IO;
procedure TaskType is
task type SHORT_LINE is
end SHORT_LINE;
task type LONG_LINE is
end LONG_LINE;
Cow, Dog, Pig : SHORT_LINE;
Elephant, Hippopotamus : LONG_LINE;
task body SHORT_LINE is
begin
for Index in 1..4 loop
delay 0.0;
Put_Line("This is a short line");
end loop;
end SHORT_LINE;
task body LONG_LINE is
begin
for Index in 1..3 loop
delay 0.0;
Put_Line("This is a much longer line to be displayed");
end loop;
end LONG_LINE;
begin
Put_Line("This is an example of use of a task type");
end TaskType;
-- Result of execution
-- This is an example of use of a task type
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- Chapter 29 - Program 2
with Ada.Text_IO;
use Ada.Text_IO;
procedure TaskArry is
task type SHORT_LINE is
end SHORT_LINE;
task type LONG_LINE is
end LONG_LINE;
Cow : array (1..4) of SHORT_LINE;
Elephant, Hippopotamus : LONG_LINE;
task body SHORT_LINE is
begin
for Index in 1..4 loop
delay 0.0;
Put_Line("This is a short line");
end loop;
end SHORT_LINE;
task body LONG_LINE is
begin
for Index in 1..3 loop
delay 0.0;
Put_Line("This is a much longer line to be displayed");
end loop;
end LONG_LINE;
begin
Put_Line("This is an example of use of a task type array");
end TaskArry;
-- Result of execution
-- This is an example of use of a task type
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- Chapter 29 - Program 3
with Ada.Text_IO;
use Ada.Text_IO;
procedure TaskAces is
task type SHORT_LINE is
end SHORT_LINE;
task type LONG_LINE is
end LONG_LINE;
type LONG_POINT is access LONG_LINE;
Cow, Dog, Pig : SHORT_LINE;
Elephant, Hippopotamus : LONG_POINT;
task body SHORT_LINE is
begin
for Index in 1..4 loop
delay 0.0;
Put_Line("This is a short line");
end loop;
end SHORT_LINE;
task body LONG_LINE is
begin
for Index in 1..3 loop
delay 0.0;
Put_Line("This is a much longer line to be displayed");
end loop;
end LONG_LINE;
begin
Put_Line("This is an example of use of a task type");
Elephant := new LONG_LINE;
Hippopotamus := new LONG_LINE;
end TaskAces;
-- Result of execution
-- This is an example of use of a task type
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a much longer line to be displayed
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a much longer line to be displayed
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- Chapter 29 - Program 4
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Parallel is
DAYS : constant := 7;
EMPLOYEES : constant := 11;
type WORKED_ARRAY is array (1..EMPLOYEES, 1..DAYS) of INTEGER;
type TOTAL_ARRAY is array (1..EMPLOYEES) of INTEGER;
Hours_Worked : WORKED_ARRAY;
Weekly_Totals : TOTAL_ARRAY;
Result : INTEGER;
task type SUMMING_TASK_TYPE is
entry Start_Sums(E_No : in INTEGER);
entry Return_Sum(Result : out INTEGER);
end SUMMING_TASK_TYPE;
Adding_Task : array(1..EMPLOYEES) of SUMMING_TASK_TYPE;
task body SUMMING_TASK_TYPE is
Total : INTEGER := 0;
Local_E_No : INTEGER;
begin
accept Start_Sums(E_No : in INTEGER) do
Local_E_No := E_No;
end Start_Sums;
for Index in 1..Days loop
Total := Total + Hours_Worked(Local_E_No, Index);
end loop;
accept Return_Sum(Result : out INTEGER) do
Result := Total;
end Return_Sum;
end SUMMING_TASK_TYPE;
begin
for Emp_Number in 1..EMPLOYEES loop
for Day in 1..DAYS loop
Hours_Worked(Emp_Number, Day) := 8;
end loop;
end loop;
Hours_Worked(2, 5) := 3;
Hours_Worked(3, 5) := 0;
Put_Line("The Hours_Worked array is filled");
-- Start all parallel additions
for Emp_Number in 1..EMPLOYEES loop
Adding_Task(Emp_Number).Start_Sums(Emp_Number);
end loop;
-- Get the results back
for Emp_Number in 1..EMPLOYEES loop
Adding_Task(Emp_Number).Return_Sum(Result);
Weekly_Totals(Emp_Number) := Result;
end loop;
for Emp_Number in 1..EMPLOYEES loop
Put("Employee number");
Put(Emp_Number, 3);
Put(" worked");
Put(Weekly_Totals(Emp_Number), 3);
Put_Line(" hours.");
end loop;
end Parallel;
-- Result of execution
-- The Hours_Worked array is filled
-- Employee number 1 worked 56 hours
-- Employee number 2 worked 51 hours
-- Employee number 3 worked 48 hours
-- Employee number 4 worked 56 hours
-- Employee number 5 worked 56 hours
-- Employee number 6 worked 56 hours
-- Employee number 7 worked 56 hours
-- Employee number 8 worked 56 hours
-- Employee number 9 worked 56 hours
-- Employee number 10 worked 56 hours
-- Employee number 11 worked 56 hours
-- Chapter 29 - Program 5
with Ada.Text_IO;
use Ada.Text_IO;
procedure Priority is
task type SHORT_LINE is
pragma PRIORITY (5);
end SHORT_LINE;
task type LONG_LINE is
pragma PRIORITY (1);
end LONG_LINE;
pragma PRIORITY (3); -- This is the priority for the main program
Cow, Dog, Pig : SHORT_LINE;
Elephant, Hippopotamus : LONG_LINE;
task body SHORT_LINE is
begin
for Index in 1..4 loop
delay 0.0;
Put_Line("This is a short line");
end loop;
end SHORT_LINE;
task body LONG_LINE is
begin
for Index in 1..3 loop
delay 0.0;
Put_Line("This is a much longer line to be displayed");
end loop;
end LONG_LINE;
begin
Put_Line("This is an example of use of a task type");
end Priority;
-- Result of execution
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is an example of use of a task type
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- Chapter 29 - Program 6
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Family is
type SPEED is (FAST, MEDIUM, SLOW);
task Selector is
entry Answer_Query(SPEED)(Counter : INTEGER);
end Selector;
procedure Output_Value(Counter : INTEGER) is
begin
Put(Counter, 3);
New_Line;
end Output_Value;
task body Selector is
begin
loop
select
accept Answer_Query(FAST)(Counter : INTEGER) do
Put("FAST Query made");
Output_Value(Counter);
end;
or
when Answer_Query(FAST)'COUNT = 0 =>
accept Answer_Query(MEDIUM)(Counter : INTEGER) do
Put("MEDIUM Query made");
Output_Value(Counter);
end;
or
when Answer_Query(FAST)'COUNT = 0 and
Answer_Query(MEDIUM)'COUNT = 0 =>
accept Answer_Query(SLOW)(Counter : INTEGER) do
Put("SLOW Query made");
Output_Value(Counter);
end;
or
terminate;
end select;
end loop;
end Selector;
begin
Put_Line("Begin the main program");
Selector.Answer_Query(FAST)(1);
Selector.Answer_Query(FAST)(2);
Selector.Answer_Query(SLOW)(3);
Selector.Answer_Query(MEDIUM)(4);
Selector.Answer_Query(SLOW)(5);
Selector.Answer_Query(MEDIUM)(6);
Selector.Answer_Query(FAST)(7);
Selector.Answer_Query(FAST)(8);
Put_Line("End of the main program");
end Family;
-- Result of Execution
-- Begin the main program
-- FAST Query made 1
-- FAST Query made 2
-- SLOW Query made 3
-- MEDIUM Query made 4
-- SLOW Query made 5
-- MEDIUM Query made 6
-- FAST Query made 7
-- FAST Query made 8
-- End of main program
-- Chapter 29 - Programming exercise 1
with Ada.Text_IO;
use Ada.Text_IO;
procedure CH29_1 is
task type SHORT_LINE is
end SHORT_LINE;
task type LONG_LINE is
end LONG_LINE;
Cow : array (1..17) of SHORT_LINE;
Elephant, Hippopotamus : LONG_LINE;
task body SHORT_LINE is
begin
for Index in 1..4 loop
delay 0.0;
Put_Line("This is a short line");
end loop;
end SHORT_LINE;
task body LONG_LINE is
begin
for Index in 1..3 loop
delay 0.0;
Put_Line("This is a much longer line to be displayed");
end loop;
end LONG_LINE;
begin
Put_Line("This is an example of use of a task type array");
end CH29_1;
-- Result of execution
-- This is an example of use of a task type
-- The output is similar to the program TASKARRY.ADA but there
-- is a lot more of it.
-- Note that 17 was the largest number that could be used for
-- the number of tasks and still execute properly. When the
-- number 18 is used, a storage_error exception is generated.
-- This is for one compiler, and the number could vary over a
-- rather large range for different compilers.
-- Chapter 29 - Programming example 2
with Ada.Text_IO;
use Ada.Text_IO;
procedure CH29_2 is
task type SHORT_LINE is
end SHORT_LINE;
task type LONG_LINE is
end LONG_LINE;
type LONG_POINT is access LONG_LINE;
Cow, Dog, Pig : SHORT_LINE;
Elephant, Hippopotamus : LONG_POINT;
Horse : LONG_POINT := new LONG_LINE;
task body SHORT_LINE is
begin
for Index in 1..4 loop
delay 0.0;
Put_Line("This is a short line");
end loop;
end SHORT_LINE;
task body LONG_LINE is
begin
for Index in 1..3 loop
delay 0.0;
Put_Line("This is a much longer line to be displayed");
end loop;
end LONG_LINE;
begin
Put_Line("This is an example of use of a task type");
Elephant := new LONG_LINE;
Hippopotamus := new LONG_LINE;
end CH29_2;
-- The task named Horse will begin execution at the same time
-- that the main body of the program begins execution.
-- Exception never handled - program_error
-- Result of execution
-- This is an example of use of a task type
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a much longer line to be displayed
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a much longer line to be displayed
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- Chapter 30 - Program 1
generic
type ITEM is range <>; -- integer class only
procedure Exchange_Data(X,Y : in out ITEM);
procedure Exchange_Data(X,Y : in out ITEM) is
Temp : ITEM;
begin
Temp := X;
X := Y;
Y := Temp;
end Exchange_Data;
-- This is the beginning of the main program which uses the generic
-- procedure defined above.
with Exchange_Data;
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure SwapSome is
type MY_INT is new INTEGER range 1..128;
procedure SwapInt is new Exchange_Data(INTEGER);
procedure SwapNew is new Exchange_Data(MY_INT);
Index1 : INTEGER := 17;
Index2 : INTEGER := 33;
Limit1 : MY_INT := 3;
Limit2 : MY_INT := 7;
begin
SwapInt(Index1, Index2);
SwapNew(Limit1, Limit2);
Put(Index1, 5);
Put(Index2, 5);
New_Line;
Put(INTEGER(Limit1), 5);
Put(INTEGER(Limit2), 5);
New_Line(2);
SwapInt(Index1, INTEGER(Limit1));
SwapNew(MY_INT(Index2), Limit2);
Put(Index1, 5);
Put(Index2, 5);
New_Line;
Put(INTEGER(Limit1), 5);
Put(INTEGER(Limit2), 5);
New_Line(2);
end SwapSome;
-- Result of Execution
-- 33 17
-- 7 3
--
-- 7 3
-- 33 17
-- Chapter 30 - Program 2
generic
type ITEM is private;
procedure Exchange_Data(X, Y : in out ITEM);
procedure Exchange_Data(X, Y : in out ITEM) is
Temp : ITEM;
begin
Temp := X;
X := Y;
Y := Temp;
end Exchange_Data;
generic
type ANOTHER_ITEM is range <>; -- Integer class only
function Average(X, Y : ANOTHER_ITEM) return ANOTHER_ITEM;
function Average(X, Y : ANOTHER_ITEM) return ANOTHER_ITEM is
Temporary : ANOTHER_ITEM;
begin
Temporary := (X + Y) / 2;
return Temporary;
end Average;
-- This is the beginning of the main program which uses the generic
-- procedure and function defined above.
with Exchange_Data, Average;
with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO;
procedure SwapMore is
type NEW_TYPE is new INTEGER range 122..12345;
type MY_RECORD is
record
My_Grade : INTEGER range 1..128;
My_Class : CHARACTER;
end record;
procedure Swap is new Exchange_Data(INTEGER);
procedure Swap is new Exchange_Data(MY_RECORD);
procedure Swap is new Exchange_Data(FLOAT);
procedure Swap is new Exchange_Data(NEW_TYPE);
procedure Swap is new Exchange_Data(CHARACTER);
procedure Trade is new Exchange_Data(CHARACTER);
procedure Exchange is new Exchange_Data(CHARACTER);
procedure Puppy is new Exchange_Data(CHARACTER);
function Mean is new Average(INTEGER);
function Mean is new Average(NEW_TYPE);
function Swap is new Average(INTEGER); -- This is dumb to do,
-- but it is legal.
Index1 : INTEGER := 117;
Index2 : INTEGER := 123;
Data1 : MY_RECORD := (15, 'A');
Data2 : MY_RECORD := (91, 'B');
Real1 : FLOAT := 3.14;
Real2 : FLOAT := 77.02;
Value1 : NEW_TYPE := 222;
Value2 : NEW_TYPE := 345;
begin
Put(Real1, 4, 2, 0);
Put(Real2, 4, 2, 0);
New_Line;
Swap(Real1, Real2);
Put(Real1, 4, 2, 0);
Put(Real2, 4, 2, 0);
New_Line(2);
Put(Index1, 6);
Put(Index2, 6);
New_Line;
Swap(Index1, Index2);
Swap(Data1, Data2);
Put(Index1, 6);
Put(Index2, 6);
New_Line;
-- Now to exercise some of the functions
Index1 := Mean(Index2, 16);
Value1 := Mean(Value2, Value2 + 132);
Index1 := Swap(Index1, Index2 + 12); -- This actually gets the
-- mean of the inputs.
end SwapMore;
-- Result of Execution
-- 3.14 77.02
-- 77.02 3.14
--
-- 117 123
-- 123 117
-- Chapter 30 - Program 3
generic
type LIMPRIV is limited private; -- Limited private type
type PRIV is private; -- Private type
type DISCRETE_TYPE is (<>); -- Discrete type
type INT_TYPE is range <>; -- Integer type
type MOD_TYPE is mod <>; -- Modular type
type FLOAT_TYPE is digits <>; -- Floating point type
type FIXED_TYPE is delta <>; -- Fixed point type
type DECIMAL_TYPE is delta <> digits <>; -- Decimal type
procedure AllGener;
procedure AllGener is
begin
null;
end AllGener;
generic
type LIMPRIV is limited private;
type PRIV is private;
type DISCRETE_TYPE is (<>);
type MOD_TYPE is mod <>;
type FLOAT_TYPE is digits <>;
type FIXED_TYPE is delta <>;
procedure ManyUsed(A, B, C : MOD_TYPE;
Data_In : LIMPRIV;
More_Dat : PRIV;
List_Of : DISCRETE_TYPE;
Real_Dat : FLOAT_TYPE;
Fix_Dat : FIXED_TYPE);
procedure ManyUsed(A, B, C : MOD_TYPE;
Data_In : LIMPRIV;
More_Dat : PRIV;
List_Of : DISCRETE_TYPE;
Real_Dat : FLOAT_TYPE;
Fix_Dat : FIXED_TYPE) is
Index2 : MOD_TYPE := 4;
Index3 : INTEGER := 7;
type NEW_INTEGER is new INTEGER range 12..34;
Small_Int : NEW_INTEGER := 17;
begin
Index2 := A + B + C + MOD_TYPE(Index3) + MOD_TYPE(Small_Int);
end ManyUsed;
-- Result of execution
-- (No results, this is not executable)
-- Chapter 30 - Program 4
-- Constrained array generic element
generic
type SUBSCRIPT is (<>);
type FLOAT_TYPE is digits <>;
type CONSTRAINED_ARRAY is array (SUBSCRIPT) of FLOAT_TYPE;
procedure SumArray(Input_Array : CONSTRAINED_ARRAY;
Sum_Of_Elements : in out FLOAT_TYPE);
procedure SumArray(Input_Array : CONSTRAINED_ARRAY;
Sum_Of_Elements : in out FLOAT_TYPE) is
begin
Sum_Of_Elements := 0.0;
for Index in Input_Array'FIRST..Input_Array'LAST loop
Sum_Of_Elements := Sum_Of_Elements + Input_Array(Index);
end loop;
end SumArray;
-- Unconstrained array generic element
generic
type MY_TYPE is range <>;
type MY_ARRAY is array (POSITIVE range <>) of MY_TYPE;
function AddArray(Input_Array : MY_ARRAY) return MY_TYPE;
function AddArray(Input_Array : MY_ARRAY) return MY_TYPE is
Sum_Of_Elements : MY_TYPE := 0;
begin
for Index in Input_Array'FIRST..Input_Array'LAST loop
Sum_Of_Elements := Sum_Of_Elements + Input_Array(Index);
end loop;
return Sum_Of_Elements;
end AddArray;
with Ada.Text_IO, SumArray, AddArray;
use Ada.Text_IO;
procedure ArrayGen is
type LIMIT_RANGE is new INTEGER range 1..5;
type MY_FLOAT is new FLOAT digits 5;
type FLOAT_ARRAY is array(LIMIT_RANGE) of MY_FLOAT;
procedure Little_Sum is new
SumArray(LIMIT_RANGE, MY_FLOAT, FLOAT_ARRAY);
Total : MY_FLOAT;
My_Data : FLOAT_ARRAY := (1.0, 2.2, 4.3, 3.1, 5.4);
type FLEX_ARRAY is array(POSITIVE range <>) of INTEGER;
Big_Data : FLEX_ARRAY(1..5) := (13, 12, 17, 33, 41);
More_Data : FLEX_ARRAY(12..14) := (34, 101, 56);
Result : INTEGER;
function My_Add is new AddArray(INTEGER, FLEX_ARRAY);
begin
Little_Sum(My_Data,Total);
Little_Sum((2.4, 1.3, 5.2, 4.7, 0.1),Total);
Result := My_Add(Big_Data);
Result := My_Add(More_Data);
end ArrayGen;
-- Result of execution
-- (No output from this program)
-- Chapter 30 - Program 5
generic
type ACCESS_INT is access INTEGER;
procedure Swap_Int_Data(Dat1, Dat2 : ACCESS_INT);
procedure Swap_Int_Data(Dat1, Dat2 : ACCESS_INT) is
Temp : ACCESS_INT := new INTEGER;
begin
Temp.all := Dat1.all;
Dat1.all := Dat2.all;
Dat2.all := Temp.all;
end Swap_Int_Data;
generic
type ANY_TYPE is private;
type ACCESS_ANY is access ANY_TYPE;
procedure Swap_Any_Data(Dat1, Dat2 : ACCESS_ANY);
procedure Swap_Any_Data(Dat1, Dat2 : ACCESS_ANY) is
Temp : ACCESS_ANY := new ANY_TYPE;
begin
Temp.all := Dat1.all;
Dat1.all := Dat2.all;
Dat2.all := Temp.all;
end Swap_Any_Data;
with Ada.Text_IO, Swap_Int_Data, Swap_Any_Data;
use Ada.Text_IO;
procedure AccesGen is
type ACCESS_INT is access INTEGER;
Address1, Address2 : ACCESS_INT := new INTEGER;
procedure Transpose_Integers is new Swap_Int_Data(ACCESS_INT);
type NAME_ARRAY is array(1..6) of CHARACTER;
type PERSONAL_STUFF is
record
Age : INTEGER;
Grade : INTEGER;
Name : NAME_ARRAY;
end record;
type PERSONAL_ACCESS is access PERSONAL_STUFF;
Male_Student : PERSONAL_ACCESS := new PERSONAL_STUFF;
Female_Student : PERSONAL_ACCESS := new PERSONAL_STUFF;
type ACCESS_FLOAT is access FLOAT;
Address_Float1 : ACCESS_FLOAT;
Address_Float2 : ACCESS_FLOAT;
procedure Transpose_Floats is new Swap_Any_Data(FLOAT,
ACCESS_FLOAT);
procedure Transpose_Records is new Swap_Any_Data(PERSONAL_STUFF,
PERSONAL_ACCESS);
procedure Transpose_Ints is new Swap_Any_Data(INTEGER, ACCESS_INT);
begin
Put_Line("Begin the generic access routine");
Address1.all := 23;
Address2.all := 13 * Address1.all;
Transpose_Integers(Address1, Address2);
Transpose_Ints(Address1, Address2);
Address_Float1 := new FLOAT;
Address_Float2 := new FLOAT;
Address_Float1.all := 3.141592;
Address_Float2.all := 144.0;
Transpose_Floats(Address_Float1, Address_Float2);
Male_Student.all := (16, 11, "Johnny");
Female_Student.all := (15, 11, "Sandy ");
Transpose_Records(Male_Student, Female_Student);
end Accesgen;
-- Result of execution
-- Begin the generic access routine
-- Chapter 30 - Programming exercise 1
generic
type ITEM is range <>; -- integer class only
procedure Exchange_Data(X,Y : in out ITEM);
procedure Exchange_Data(X,Y : in out ITEM) is
Temp : ITEM;
begin
Temp := X;
X := Y;
Y := Temp;
end Exchange_Data;
-- This is the beginning of the main program which uses the generic
-- procedure defined above.
with Exchange_Data;
with Ada.Text_IO;
use Ada.Text_IO;
procedure CH30_1 is
package Int_IO is new Ada.Text_IO.Integer_IO(INTEGER);
use Int_IO;
type MY_INT is new INTEGER range 1..128;
subtype SUB_INT is INTEGER range 1.. 55;
procedure SwapInt is new Exchange_Data(INTEGER);
procedure SwapNew is new Exchange_Data(MY_INT);
procedure SwapSub is new Exchange_Data(SUB_INT);
Index1 : INTEGER := 17;
Index2 : INTEGER := 33;
Limit1 : MY_INT := 3;
Limit2 : MY_INT := 7;
Small1 : SUB_INT := 13;
Small2 : SUB_INT := 27;
begin
SwapInt(Index1,Index2);
SwapNew(Limit1,Limit2);
SwapSub(Small1,Small2);
SwapInt(Small1,Small2);
Put(Index1);
Put(Index2);
New_Line;
Put(INTEGER(Limit1));
Put(INTEGER(Limit2));
New_Line(2);
SwapInt(Index1,INTEGER(Limit1));
SwapNew(MY_INT(Index2),Limit2);
Put(Index1);
Put(Index2);
New_Line;
Put(INTEGER(Limit1));
Put(INTEGER(Limit2));
New_Line(2);
end CH30_1;
-- Note that it is legal to instantiate a copy of the generic
-- procedure for the subtype. It is a waste of code however,
-- because the procedure for the parent type can be used for
-- the subtype as illustrated in line 44.
-- Result of Execution
-- 33 17
-- 7 3
--
-- 7 3
-- 33 17
-- Chapter 30 - Programming exercise 2
generic
type ITEM is range <>; -- integer class only
procedure Exchange_Data(X,Y : in out ITEM);
procedure Exchange_Data(X,Y : in out ITEM) is
Temp : ITEM;
begin
Temp := X;
X := Y;
Y := Temp;
end Exchange_Data;
-- This is the beginning of the main program which uses the generic
-- procedure defined above.
with Exchange_Data;
with Ada.Text_IO;
use Ada.Text_IO;
procedure CH30_2 is
package Int_IO is new Ada.Text_IO.Integer_IO(INTEGER);
use Int_IO;
type MY_INT is new INTEGER range 1..128;
procedure SwapInt is new Exchange_Data(INTEGER);
procedure SwapNew is new Exchange_Data(MY_INT);
procedure SwapFlt is new Exchange_Data(FLOAT);
Index1 : INTEGER := 17;
Index2 : INTEGER := 33;
Limit1 : MY_INT := 3;
Limit2 : MY_INT := 7;
begin
SwapInt(Index1, Index2);
SwapNew(Limit1, Limit2);
Put(Index1);
Put(Index2);
New_Line;
Put(INTEGER(Limit1));
Put(INTEGER(Limit2));
New_Line(2);
SwapInt(Index1, INTEGER(Limit1));
SwapNew(MY_INT(Index2), Limit2);
Put(Index1);
Put(Index2);
New_Line;
Put(INTEGER(Limit1));
Put(INTEGER(Limit2));
New_Line(2);
end CH30_2;
-- Result of Execution
-- Compile error,
-- Line 29 - must instantiate with integer type for "ITEM".
-- Chapter 30 - Programming exercise 3
generic
type ITEM is private;
procedure Exchange_Data(X, Y : in out ITEM);
procedure Exchange_Data(X, Y : in out ITEM) is
Temp : ITEM;
begin
Temp := X + 1; -- This should produce an error
X := Y;
Y := Temp;
end Exchange_Data;
generic
type ANOTHER_ITEM is range <>; -- Integer class only
function Average(X, Y : ANOTHER_ITEM) return ANOTHER_ITEM;
function Average(X, Y : ANOTHER_ITEM) return ANOTHER_ITEM is
Temporary : ANOTHER_ITEM;
begin
Temporary := (X + Y) / 2;
return Temporary;
end Average;
-- This is the beginning of the main program which uses the generic
-- procedure and function defined above.
with Exchange_Data, Average;
with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO;
procedure CH30_3 is
type NEW_TYPE is new INTEGER range 122..12345;
type MY_RECORD is
record
My_Grade : INTEGER range 1..128;
My_Class : CHARACTER;
end record;
procedure Swap is new Exchange_Data(INTEGER);
procedure Swap is new Exchange_Data(MY_RECORD);
procedure Swap is new Exchange_Data(FLOAT);
procedure Swap is new Exchange_Data(NEW_TYPE);
procedure Swap is new Exchange_Data(CHARACTER);
procedure Trade is new Exchange_Data(CHARACTER);
procedure Exchange is new Exchange_Data(CHARACTER);
procedure Puppy is new Exchange_Data(CHARACTER);
function Mean is new Average(INTEGER);
function Mean is new Average(NEW_TYPE);
function Swap is new Average(INTEGER); -- This is dumb to do,
-- but it is legal.
Index1 : INTEGER := 117;
Index2 : INTEGER := 123;
Data1 : MY_RECORD := (15,'A');
Data2 : MY_RECORD := (91,'B');
Real1 : FLOAT := 3.14;
Real2 : FLOAT := 77.02;
Value1 : NEW_TYPE := 222;
Value2 : NEW_TYPE := 345;
begin
Put(Real1, 4, 2, 0);
Put(Real2, 4, 2, 0);
New_Line;
Swap(Real1, Real2);
Put(Real1, 4, 2, 0);
Put(Real2, 4, 2, 0);
New_Line(2);
Put(Index1);
Put(Index2);
New_Line;
Swap(Index1, Index2);
Swap(Data1, Data2);
Put(Index1);
Put(Index2);
New_Line;
-- Now to exercise some of the functions
Index1 := Mean(Index2, 16);
Value1 := Mean(Value2, Value2 + 132);
Index1 := Swap(Index1, Index2 + 12); -- This actually gets the
-- mean of the inputs.
end CH30_3;
-- Result of Execution
-- Compile error,
-- Line 9 - Illegal operation for a private type, type clash.
-- Chapter 31 - Program 1
generic
type MY_INT_TYPE is (<>);
type MY_REAL_TYPE is digits <>;
package EasyPkg is
procedure Trade_Values (X, Y : in out MY_INT_TYPE);
function Average_Values (X, Y : MY_REAL_TYPE)
return MY_REAL_TYPE;
end EasyPkg;
package body EasyPkg is
procedure Trade_Values (X, Y : in out MY_INT_TYPE) is
Temp : MY_INT_TYPE;
begin
Temp := X;
X := Y;
Y := Temp;
end Trade_Values;
function Average_Values (X, Y : MY_REAL_TYPE)
return MY_REAL_TYPE is
begin
return (X + Y) / 2.0;
end Average_Values;
end EasyPkg;
with Ada.Text_IO, EasyPkg;
use Ada.Text_IO;
procedure GenPkg is
type MY_NEW_TYPE is new INTEGER range -12..123;
type MY_NEW_FLOAT is new FLOAT digits 6;
package Funny_Stuff is new EasyPkg(MY_NEW_TYPE, MY_NEW_FLOAT);
use Funny_Stuff;
package Usual_Stuff is new EasyPkg(INTEGER, FLOAT);
use Usual_Stuff;
Int1 : INTEGER := 12;
Int2 : INTEGER := 35;
My_Int1 : MY_NEW_TYPE := 1;
My_Int2 : MY_NEW_TYPE := 14;
Real1 : FLOAT;
My_Real1 : MY_NEW_FLOAT;
begin
Trade_Values(Int1, Int2); -- Uses Usual_Stuff.Trade_Values
Trade_Values(My_Int1, My_Int2); -- Uses Funny_Stuff.Trade_Values
Usual_Stuff.Trade_Values(Int1, Int2);
Funny_Stuff.Trade_Values(My_Int1, My_Int2);
-- Usual_Stuff.Trade_Values(My_Int1, My_Int2); -- Illegal
-- Trade_Values(My_Int1, Int2); -- Illegal
Real1 := Average_Values(2.71828, 3.141592);
Real1 := Average_Values(Real1, 2.0 * 3.141592);
My_Real1 := Average_Values(12.3, 27.345);
My_Real1 := Average_Values(My_Real1, 2.0 * 3.141592);
My_Real1 := Funny_Stuff.Average_Values(12.3, 27.345);
end GenPkg;
-- Result of execution
-- (There is no output from this program)
-- Chapter 31 - Program 2
package EasyPkg is
procedure Trade_Values (X, Y : in out INTEGER);
generic
type MY_REAL_TYPE is digits <>;
package Nested_Generic is
function Average_Values (X, Y : MY_REAL_TYPE)
return MY_REAL_TYPE;
end Nested_Generic;
end EasyPkg;
package body EasyPkg is
procedure Trade_Values (X, Y : in out INTEGER) is
Temp : INTEGER;
begin
Temp := X;
X := Y;
Y := Temp;
end Trade_Values;
package body Nested_Generic is
function Average_Values (X, Y : MY_REAL_TYPE)
return MY_REAL_TYPE is
begin
return (X + Y) / 2.0;
end Average_Values;
end Nested_Generic;
end EasyPkg;
with Ada.Text_IO, EasyPkg;
use Ada.Text_IO, EasyPkg;
procedure NestPkg is
type MY_NEW_FLOAT is new FLOAT digits 6;
package Funny_Stuff is new EasyPkg.Nested_Generic(MY_NEW_FLOAT);
use Funny_Stuff;
package Usual_Stuff is new Nested_Generic(FLOAT);
use Usual_Stuff;
Int1 : INTEGER := 12;
Int2 : INTEGER := 35;
Real1 : FLOAT;
My_Real1 : MY_NEW_FLOAT;
begin
Trade_Values(Int1, Int2); -- Uses Trade_Values directly
EasyPkg.Trade_Values(Int1, Int2); -- Uses Trade_Values directly
-- Usual_Stuff.Trade_Values(Int1, Int2); -- Illegal
-- Funny_Stuff.Trade_Values(Int1, Int2); -- Illegal
Real1 := Average_Values(2.71828, 3.141592);
Real1 := Average_Values(Real1, 2.0 * 3.141592);
My_Real1 := Average_Values(12.3, 27.345);
My_Real1 := Average_Values(My_Real1, 2.0 * 3.141592);
My_Real1 := Funny_Stuff.Average_Values(12.3, 27.345);
end NestPkg;
-- Result of execution
-- (There is no output from this program)
-- Chapter 31 - Program 3
generic
type ITEM is range <>;
ROWS : in POSITIVE := 8;
COLUMNS : in POSITIVE := 12;
package Matrix_Operations is
type LOCAL_MATRIX is private;
procedure Clear_Matrix(Matrix_To_Clear : in out LOCAL_MATRIX);
function Add_Matrices(M1, M2 : LOCAL_MATRIX) return LOCAL_MATRIX;
private
type LOCAL_MATRIX is array(POSITIVE range 1..ROWS,
POSITIVE range 1..COLUMNS) of ITEM;
end Matrix_Operations;
with Ada.Text_IO;
use Ada.Text_IO;
package body Matrix_Operations is
procedure Clear_Matrix(Matrix_To_Clear : in out LOCAL_MATRIX) is
begin
for Row in 1..Matrix_To_Clear'LAST(1) loop
for column in 1.. Matrix_To_Clear'LAST(2) loop
Matrix_To_Clear(Row,Column) := 0;
end loop;
end loop;
Put_Line(" A matrix has been cleared");
end Clear_Matrix;
function Add_Matrices(M1, M2 : LOCAL_MATRIX) return LOCAL_MATRIX is
Temp : LOCAL_MATRIX;
begin
for Row in 1..M1'LAST(1) loop
for column in 1.. M1'LAST(2) loop
Temp(Row, Column) := M1(Row, Column) + M2(Row, Column);
end loop;
end loop;
Put_Line(" Two matrices have been summed");
return Temp;
end Add_Matrices;
end Matrix_Operations;
with Ada.Text_IO, Matrix_Operations;
use Ada.Text_IO;
procedure ObjGen is
package Cookie_Jar is new Matrix_Operations(NATURAL, 3, 5);
use Cookie_Jar;
package Puppies is new Matrix_Operations(INTEGER);
package Animals is new Matrix_Operations(COLUMNS => 7,
ROWS => 11,
ITEM => INTEGER);
use Animals;
Cookie_Matrix : Cookie_Jar.LOCAL_MATRIX;
Dog_Matrix : Puppies.LOCAL_MATRIX;
Cattle, Jerseys, Black_Angus : Animals.LOCAL_MATRIX;
begin
Put_Line("Begin the matrix operations.");
Clear_Matrix(Cookie_Matrix);
Puppies.Clear_Matrix(Dog_Matrix);
Clear_Matrix(Jerseys);
Clear_Matrix(Black_Angus);
Cattle := Add_Matrices(Jerseys, Black_Angus);
end ObjGen;
-- Result of execution
-- Begin the matrix operations
-- A matrix has been cleared
-- A matrix has been cleared
-- A matrix has been cleared
-- A matrix has been cleared
-- Two matrices have been summed
-- Chapter 31 - Program 4
generic
type ITEM is range <>;
with procedure Funny_Average(A, B, C : ITEM;
Result : in out ITEM);
package Dis_Avg is
procedure Display_Funny_Average(A, B, C : ITEM);
end Dis_Avg;
with Ada.Text_IO;
use Ada.Text_IO;
package body Dis_Avg is
package Int_IO is new Ada.Text_IO.Integer_IO(ITEM);
use Int_IO;
procedure Display_Funny_Average(A, B, C : ITEM) is
Mean : ITEM;
begin
Funny_Average(A, B, C, Mean);
Put("The funny average is ");
Put(Mean, 5);
New_Line;
end Display_Funny_Average;
end Dis_Avg;
with Dis_Avg;
procedure ProcPkg is
procedure R_Average(First, Second, Third : INTEGER;
Weighted_Average : in out INTEGER) is
begin
Weighted_Average := (First + 2 * Second + Third) / 4;
end R_Average;
package Averages is new Dis_Avg(INTEGER, R_Average);
begin
Averages.Display_Funny_Average(1, 3, 5);
Averages.Display_Funny_Average(1, 3, 17);
Averages.Display_Funny_Average(3, 17, 5);
end ProcPkg;
-- Result of execution
-- The funny average is 3
-- The funny average is 6
-- The funny average is 10
-- Chapter 31 - Program 5
generic
type ITEM is range <>;
with function Funny_Average(A, B, C : ITEM) return ITEM;
package Dis_Avg is
procedure Display_Funny_Average(A, B, C : ITEM);
end Dis_Avg;
with Ada.Text_IO;
use Ada.Text_IO;
package body Dis_Avg is
package Int_IO is new Ada.Text_IO.Integer_IO(ITEM);
use Int_IO;
procedure Display_Funny_Average(A, B, C : ITEM) is
Mean : ITEM;
begin
Mean := Funny_Average(A, B, C);
Put("The funny average is ");
Put(Mean, 5);
New_Line;
end Display_Funny_Average;
end Dis_Avg;
with Dis_Avg;
procedure FuncPkg is
function R_Average(First, Second, Third : INTEGER)
return INTEGER is
begin
return (First + 2 * Second + Third) / 4;
end R_Average;
package Averages is new Dis_Avg(INTEGER, R_Average);
begin
Averages.Display_Funny_Average(1, 3, 5);
Averages.Display_Funny_Average(1, 3, 17);
Averages.Display_Funny_Average(3, 17, 5);
end FuncPkg;
-- Result of execution
-- The funny average is 3
-- The funny average is 6
-- The funny average is 10
-- Chapter 31 - Programming exercise 1
generic
type MY_INT_TYPE is (<>);
type MY_REAL_TYPE is digits <>;
package EasyPkg is
procedure Trade_Values (X, Y : in out MY_INT_TYPE);
function Average_Values (X, Y : MY_REAL_TYPE)
return MY_REAL_TYPE;
end EasyPkg;
package body EasyPkg is
procedure Trade_Values (X, Y : in out MY_INT_TYPE) is
Temp : MY_INT_TYPE;
begin
Temp := X;
X := Y;
Y := Temp;
end Trade_Values;
function Average_Values (X, Y : MY_REAL_TYPE)
return MY_REAL_TYPE is
begin
return (X + Y) / 2.0;
end Average_Values;
end EasyPkg;
with Ada.Text_IO, EasyPkg;
use Ada.Text_IO;
procedure CH31_1 is
type MY_NEW_TYPE is new INTEGER range -12..123;
type MY_NEW_FLOAT is new FLOAT digits 6;
type RESULT is (WIN, LOSE, DRAW, FORFEIT, CANCELLED);
package Funny_Stuff is new EasyPkg(MY_NEW_TYPE, MY_NEW_FLOAT);
use Funny_Stuff;
package Usual_Stuff is new EasyPkg(INTEGER, FLOAT);
use Usual_Stuff;
package Ball_Game is new EasyPkg(RESULT, FLOAT);
use Ball_Game;
Int1 : INTEGER := 12;
Int2 : INTEGER := 35;
My_Int1 : MY_NEW_TYPE := 1;
My_Int2 : MY_NEW_TYPE := 14;
Game1 : RESULT := WIN;
Game2 : RESULT := FORFEIT;
Real1 : FLOAT;
My_Real1 : MY_NEW_FLOAT;
begin
Ball_Game.Trade_Values(Game1, Game2);
Trade_Values(Game1, Game2);
Trade_Values(Int1, Int2); -- Uses Usual_Stuff.Trade_Values
Trade_Values(My_Int1, My_Int2); -- Uses Funny_Stuff.Trade_Values
Usual_Stuff.Trade_Values(Int1, Int2);
Funny_Stuff.Trade_Values(My_Int1, My_Int2);
-- Usual_Stuff.Trade_Values(My_Int1, My_Int2); -- Illegal
-- Trade_Values(My_Int1, Int2); -- Illegal
Real1 := Usual_Stuff.Average_Values(2.71828, 3.141592);
Real1 := Usual_Stuff.Average_Values(Real1, 2.0 * 3.141592);
My_Real1 := Average_Values(12.3, 27.345);
My_Real1 := Average_Values(My_Real1, 2.0 * 3.141592);
My_Real1 := Funny_Stuff.Average_Values(12.3, 27.345);
end CH31_1;
-- Result of execution
-- (There is no output from this program)
-- Chapter 31 - Programming exercise 2
generic
type ITEM is (<>);
package CharStak is
procedure Push(In_Char : in ITEM); -- In_Char is added to the
-- stack if there is room.
procedure Pop(Out_Char : out ITEM); -- Out_Char is removed from
-- stack and returned if a
-- character is on stack.
-- else a blank is returned
function Is_Empty return BOOLEAN; -- TRUE if stack is empty
function Is_Full return BOOLEAN; -- TRUE if stack is full
function Current_Stack_Size return INTEGER;
procedure Clear_Stack; -- Reset the stack to empty
end CharStak;
package body CharStak is
Maximum_Size : constant := 25;
Stack_List : array(1..Maximum_Size)
of ITEM; -- The stack itself, purposely
-- defined very small.
Top_Of_Stack : INTEGER := 0; -- This will always point to
-- the top entry on the stack.
procedure Push(In_Char : in ITEM) is
begin
if not Is_Full then
Top_Of_Stack := Top_Of_Stack + 1;
Stack_List(Top_Of_Stack) := In_Char;
end if;
end Push;
procedure Pop(Out_Char : out ITEM) is
begin
if Is_Empty then
null; -- Cannot return a blank for a generic routine.
else
Out_Char := Stack_List(Top_Of_Stack);
Top_Of_Stack := Top_Of_Stack - 1;
end if;
end Pop;
function Is_Empty return BOOLEAN is
begin
return Top_Of_Stack = 0;
end Is_Empty;
function Is_Full return BOOLEAN is
begin
return Top_Of_Stack = Maximum_Size;
end Is_Full;
function Current_Stack_Size return INTEGER is
begin
return Top_Of_Stack;
end Current_Stack_Size;
procedure Clear_Stack is
begin
Top_Of_Stack := 0;
end Clear_Stack;
end CharStak;
-- Chapter 31 - Programming exercise 2
with Ada.Text_IO;
use Ada.Text_IO;
with CharStak;
procedure CH31_2B is
package Int_IO is new Ada.Text_IO.Integer_IO(INTEGER);
use Int_IO;
package Stack is new CharStak(CHARACTER);
use Stack;
Example : constant STRING := "This is the first test.";
Another : constant STRING :=
"This is another test and this should not fit.";
procedure Fill_The_Stack(Input_Line : STRING) is
begin
Clear_Stack;
for Index in 1..Input_Line'LAST loop
if Is_Full then
Put_Line("The stack is full, no more added.");
exit;
else
Push(Input_Line(Index));
end if;
end loop;
end Fill_The_Stack;
procedure Empty_The_Stack is
Char : CHARACTER;
begin
loop
if Is_Empty then
New_Line;
Put_Line("The stack is empty.");
exit;
else
Pop(Char);
Put(Char);
end if;
end loop;
end Empty_The_Stack;
begin
Put_Line(Example);
Fill_The_Stack(Example);
Empty_The_Stack;
New_Line;
Put_Line(Another);
Fill_The_Stack(Another);
Empty_The_Stack;
end CH31_2B;
-- Result of execution
-- This is the first test.
-- .tset tsrif eht si sihT
-- The stack is empty.
--
-- This is another test and should not fit.
-- The stack is full, no more added.
-- dna tset rehtona si sihT
-- The stack is empty.
-- Chapter 32 - Program 1
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure SmallInt is
BITS : constant := 1;
type SMALL_INTEGER is new INTEGER range -25..120;
for SMALL_INTEGER'SIZE use 8*BITS;
type BIG_ARRAY is array(1..1000) of SMALL_INTEGER;
Number : SMALL_INTEGER := 27;
Big_List : BIG_ARRAY;
begin
Put("The type SMALL_INTEGER uses ");
Put(INTEGER(SMALL_INTEGER'SIZE), 5);
Put(" bits of memory.");
New_Line;
Put("The type BIG_ARRAY uses ");
Put(INTEGER(BIG_ARRAY'SIZE), 5);
Put(" bits of memory.");
New_Line;
end SmallInt;
-- Result of execution (as written)
-- The type SMALL_INTEGER uses 8 bits of memory.
-- The type BIG_ARRAY uses 8000 bits of memory.
-- Result of execution (with line 9 commented out)
-- The type SMALL_INTEGER uses 32 bits of memory.
-- The type BIG_ARRAY uses 32000 bits of memory.
-- Chapter 32 - Program 2
with Ada.Text_IO, Ada.Integer_Text_IO, Unchecked_Conversion;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure BitField is
type BITS is record
Lower : INTEGER range 0..3;
Middle : INTEGER range 0..1;
High : INTEGER range 0..3;
end record;
for BITS use record
Lower at 0 range 0..1;
Middle at 0 range 2..2;
High at 0 range 3..4;
end record;
BitData : BITS;
IntData : INTEGER;
function Switch_To_Bits is new Unchecked_Conversion(
Source => INTEGER,
Target => BITS);
begin
BitData.Lower := 2;
BitData.High := 3;
BitData.Middle := 0;
for Index in 1..18 loop
IntData := Index + 5;
BitData := Switch_To_Bits(IntData);
Put("IntData =");
Put(IntData, 4);
Put(" BitData =");
Put(INTEGER(BitData.High), 3);
Put(INTEGER(BitData.Middle), 3);
Put(INTEGER(BitData.Lower), 3);
New_Line;
end loop;
end BitField;
-- Result of Execution
-- IntData = 6 BitData = 0 1 2
-- IntData = 7 BitData = 0 1 3
-- IntData = 8 BitData = 1 0 0
-- IntData = 9 BitData = 1 0 1
-- IntData = 10 BitData = 1 0 2
-- IntData = 11 BitData = 1 0 3
-- IntData = 12 BitData = 1 1 0
-- IntData = 13 BitData = 1 1 1
-- IntData = 14 BitData = 1 1 2
-- IntData = 15 BitData = 1 1 3
-- IntData = 16 BitData = 2 0 0
-- IntData = 17 BitData = 2 0 1
-- IntData = 18 BitData = 2 0 2
-- IntData = 19 BitData = 2 0 3
-- IntData = 20 BitData = 2 1 0
-- IntData = 21 BitData = 2 1 1
-- IntData = 22 BitData = 2 1 2
-- IntData = 23 BitData = 2 1 3
-- Chapter 32 - Program 3
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure PackItIn is
type LITTLE1 is range 1..57;
type LITTLE_REC1 is
record
A, B, C : LITTLE1;
end record;
type LIST1 is array(1..5) of LITTLE_REC1;
type LITTLE2 is range 1..57;
type LITTLE_REC2 is
record
A, B, C : LITTLE2;
end record;
pragma PACK(LITTLE_REC2);
type LIST2 is array(1..5) of LITTLE_REC2;
pragma PACK(LIST2);
type LITTLE3 is range 1..57;
for LITTLE3'SIZE use 8;
type LITTLE_REC3 is
record
A, B, C : LITTLE3;
end record;
pragma PACK(LITTLE_REC3);
type LIST3 is array(1..5) of LITTLE_REC3;
pragma PACK(LIST3);
begin
Put("Type LITTLE1 uses ");
Put(LITTLE1'SIZE, 5);
Put_Line(" bits for its representation.");
Put("Type LITTLE_REC1 uses ");
Put(LITTLE_REC1'SIZE, 5);
Put_Line(" bits for its representation.");
Put("Type LIST1 uses ");
Put(LIST1'SIZE, 5);
Put_Line(" bits for its representation.");
Put("Type LITTLE2 uses ");
Put(LITTLE2'SIZE, 5);
Put_Line(" bits for its representation.");
Put("Type LITTLE_REC2 uses ");
Put(LITTLE_REC2'SIZE, 5);
Put_Line(" bits for its representation.");
Put("Type LIST2 uses ");
Put(LIST2'SIZE, 5);
Put_Line(" bits for its representation.");
Put("Type LITTLE3 uses ");
Put(LITTLE3'SIZE, 5);
Put_Line(" bits for its representation.");
Put("Type LITTLE_REC3 uses ");
Put(LITTLE_REC3'SIZE, 5);
Put_Line(" bits for its representation.");
Put("Type LIST3 uses ");
Put(LIST3'SIZE, 5);
Put_Line(" bits for its representation.");
end PackItIn;
-- Result of execution, Compiler 1. (Did not support Line 28)
-- Type LITTLE1 uses 16 bits for its representation.
-- Type LITTLE_REC1 uses 48 bits for its representation.
-- Type LIST1 uses 240 bits for its representation.
-- Type LITTLE2 uses 16 bits for its representation.
-- Type LITTLE_REC2 uses 32 bits for its representation.
-- Type LIST2 uses 160 bits for its representation.
-- Type LITTLE3 uses 16 bits for its representation.
-- Type LITTLE_REC3 uses 32 bits for its representation.
-- Type LIST3 uses 160 bits for its representation.
-- Result of execution, Compiler 2. (Did support Line 28)
-- Type LITTLE1 uses 6 bits for its representation.
-- Type LITTLE_REC1 uses 24 bits for its representation.
-- Type LIST1 uses 120 bits for its representation.
-- Type LITTLE2 uses 6 bits for its representation.
-- Type LITTLE_REC2 uses 22 bits for its representation.
-- Type LIST2 uses 120 bits for its representation.
-- Type LITTLE3 uses 8 bits for its representation.
-- Type LITTLE_REC3 uses 24 bits for its representation.
-- Type LIST3 uses 120 bits for its representation.
-- Chapter 32 - Program 4
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, ada.Integer_Text_IO;
procedure EnumRep is
type MOTION is (STOPPED, NORTH, EAST, SOUTH, WEST);
for MOTION use (STOPPED => 0,
NORTH => 1,
EAST => 2,
SOUTH => 4,
WEST => 8);
Robot_Direction : MOTION := STOPPED;
begin
Robot_Direction := MOTION'LAST; -- WEST
Robot_Direction := MOTION'PRED(Robot_Direction); -- SOUTH
end EnumRep;
-- Result of execution
-- (There is no output from this program)
-- Chapter 32 - Programming exercise 1
with Ada.Text_IO, Unchecked_Conversion;
use Ada.Text_IO;
procedure CH32_1 is
package Int_IO is new Ada.Text_IO.Integer_IO(INTEGER);
use Int_IO;
NUMBER_OF_BYTES : constant := 4;
type CHAR_ARRAY is array(1..NUMBER_OF_BYTES) of CHARACTER;
Split_Array : CHAR_ARRAY;
Int_Data : INTEGER;
function Switch_To_Bits is new Unchecked_Conversion(
Source => INTEGER,
Target => CHAR_ARRAY);
begin
for Index in 253..260 loop
Int_Data := Index;
Split_Array := Switch_To_Bits(Int_Data);
Put("Int_Data =");
Put(Int_Data, 6);
Put(" Split_Array =");
for Count in 1..NUMBER_OF_BYTES loop
Put(CHARACTER'POS(Split_Array(Count)), 4);
end loop;
New_Line;
end loop;
end CH32_1;
-- Result of Execution
-- Int_Data = 253 Split_Array = 253 0 0 0
-- Int_Data = 254 Split_Array = 254 0 0 0
-- Int_Data = 255 Split_Array = 255 0 0 0
-- Int_Data = 256 Split_Array = 0 1 0 0
-- Int_Data = 257 Split_Array = 1 1 0 0
-- Int_Data = 258 Split_Array = 2 1 0 0
-- Int_Data = 259 Split_Array = 3 1 0 0
-- Int_Data = 260 Split_Array = 4 1 0 0
-- Note that the fields are apparently reversed because of the
-- way the bytes are stored in the microprocessor used for this
-- example program.
-- Chapter 32 - Programming exercise 2
with Ada.Text_IO, Unchecked_Conversion;
use Ada.Text_IO;
procedure CH32_2 is
package Int_IO is new Ada.Text_IO.Integer_IO(INTEGER);
use Int_IO;
package Flt_IO is new Ada.Text_IO.Float_IO(FLOAT);
use Flt_IO;
NUMBER_OF_BYTES : constant := 4;
type CHAR_ARRAY is array(1..NUMBER_OF_BYTES) of CHARACTER;
Split_Array : CHAR_ARRAY;
Float_Data : FLOAT := 1.0;
function Switch_To_Bits is new Unchecked_Conversion(
Source => FLOAT,
Target => CHAR_ARRAY);
begin
for Index in 1..8 loop
Split_Array := Switch_To_Bits(Float_Data);
Put("Float_Data =");
Put(Float_Data, 6, 2, 0);
Put(" Split_Array =");
for Count in 1..NUMBER_OF_BYTES loop
Put(CHARACTER'POS(Split_Array(Count)), 4);
end loop;
New_Line;
Float_Data := Float_Data * 2.0;
end loop;
end CH32_2;
-- Result of Execution
-- Float_Data = 1.00 Split_Array = 0 0 128 63
-- Float_Data = 2.00 Split_Array = 0 0 0 64
-- Float_Data = 4.00 Split_Array = 0 0 128 64
-- Float_Data = 8.00 Split_Array = 0 0 0 65
-- Float_Data = 16.00 Split_Array = 0 0 128 65
-- Float_Data = 32.00 Split_Array = 0 0 0 66
-- Float_Data = 64.00 Split_Array = 0 0 128 66
-- Float_Data = 128.00 Split_Array = 0 0 0 67
-- Note that the fields are apparently reversed because of the
-- way the bytes are stored in the microprocessor used for this
-- example program.
-- Chapter 33 - Program 1
-- This is a generic package to generate random numbers in the
-- range of 0.00000 to just less than 1.00000 with as many
-- significant digits as the type FLOAT_ITEM. This package uses
-- the Linear Congruential Method of random number generation as
-- discussed in "The Art of Computer Programming" volume 2 by
-- Donald Knuth. The method used follows;
--
-- X(n + 1) = (A * X(n) + C) mod M
--
-- X(n + 1) is the new random number
-- X(n) is the previous random number or the seed
-- M is 1.0 for the floating point system
-- A is 7.0 for the floating point system
-- C is 13.0 / 31.0 for the floating point system
-- X(0) is zero by default
-- X(0) is the number provided if forced with Force_Seed
-- X(0) is generated as follows when Set_Seed is called;
-- 1. The real time clock is read from the system
-- 2. The hours and minutes are stripped off
-- 3. The resulting number of seconds are divided by
-- 60.0 to get the fraction of a minute that has
-- elapsed since midnight
generic
type FLOAT_ITEM is digits <>;
package Random is
-- This procedure uses the system clock to set the seed.
procedure Set_Seed;
-- This procedure sets the seed to the input value.
procedure Force_Seed(Start_Seed : FLOAT_ITEM);
-- This Function returns the current seed which is also
-- the value of the previous random number returned.
function Get_Seed return FLOAT_ITEM;
-- This function returns a random number from 0.0 to 1.0
function Random_Number return FLOAT_ITEM;
end Random;
with Ada.Text_IO, Calendar;
use Ada.Text_IO, Calendar;
package body Random is
X_initial : FLOAT_ITEM := 0.0;
M : FLOAT_ITEM := 1.0;
A : FLOAT_ITEM := 7.0;
C : FLOAT_ITEM := 13.0 / 31.0;
procedure Set_Seed is
Time_And_Date : TIME;
All_Day : DAY_DURATION;
Minutes : FLOAT_ITEM;
Int_Minutes : INTEGER;
Part_Of_A_Minute : FLOAT_ITEM;
begin
Time_And_Date := Clock; -- Get the time and date
All_Day := Seconds(Time_And_Date); -- Seconds since midnight
Minutes := FLOAT_ITEM(All_Day) / 60.0; -- Floating type Minutes
Int_Minutes := INTEGER(Minutes - 0.5); -- Integer type minutes
Part_Of_A_Minute := FLOAT_ITEM(All_Day)
- 60.0 * FLOAT_ITEM(Int_Minutes);
X_Initial := Part_Of_A_Minute / 60.0;
end Set_Seed;
procedure Force_Seed(Start_Seed : FLOAT_ITEM) is
Temp : FLOAT_ITEM;
Natural_Temp : NATURAL;
begin
Natural_Temp := NATURAL(Start_Seed - 0.5); -- Subtract 0.5 because
-- the type conversion
-- rounds the result.
Temp := Start_Seed - FLOAT_ITEM(Natural_Temp);
X_Initial := Start_Seed;
exception
when Constraint_Error =>
Put_Line("Seed out of range, ignored");
end Force_Seed;
function Get_Seed return FLOAT_ITEM is
begin
return X_Initial;
end Get_Seed;
function Random_Number return FLOAT_ITEM is
Temp : FLOAT_ITEM;
Natural_Temp : NATURAL; -- Cannot exceed (7 + 13/31)
begin
Temp := A * X_Initial + C;
Natural_Temp := NATURAL(Temp - 0.5); -- Subtract 0.5 because
-- the type conversion
-- rounds the result.
Temp := Temp - FLOAT_ITEM(Natural_Temp);
X_Initial := Temp;
return Temp;
end Random_Number;
end Random;
-- Chapter 33 - Program 2
with Ada.Text_IO, Random;
use Ada.Text_IO;
procedure TestRan is
package My_Random is new Random(FLOAT);
use My_Random;
package Int_IO is new Ada.Text_IO.Integer_IO(INTEGER);
use Int_IO;
package Flt_IO is new Ada.Text_IO.Float_IO(FLOAT);
use Flt_IO;
SIZE : constant := 100;
type MY_ARRAY is array(1..SIZE) of INTEGER;
Events : MY_ARRAY;
Int_Rand : INTEGER;
begin
Set_Seed;
Put("The starting value of the seed is ");
Put(Get_Seed, 3, 6, 0);
New_Line;
for Index in 1..12 loop
Put("The random number is ");
Put(Random_Number, 3, 6, 0);
New_Line;
end loop;
for Index in 1..SIZE loop
Events(Index) := 0;
end loop;
for Index in 1..10000 loop
Int_Rand := INTEGER(0.5 + (FLOAT(SIZE) * Random_Number));
Events(Int_Rand) := Events(Int_Rand) + 1;
end loop;
New_Line(2);
for Index in 1..Size loop
Put(Events(Index), 4);
end loop;
end TestRan;
-- Result of execution
-- The starting value of the seed is 0.195333
-- The random number is 0.786685
-- The random number is 0.926148
-- The random number is 0.902392
-- The random number is 0.736096
-- The random number is 0.572030
-- The random number is 0.423565
-- The random number is 0.384310
-- The random number is 0.109521
-- The random number is 0.186004
-- The random number is 0.721385
-- The random number is 0.469050
-- The random number is 0.702704
-- 109 97 102 96 107 98 85 87 104 111 113 115 108 99 112 ...
-- 97 93 107 91 101 85 91 103 95 101 102 98 95 118 110 ...
-- 87 106 103 102 93 129 112 94 102 89 95 104 98 94 98 ...
-- 93 106 96 104 119 95 82 97 112 82 104 103 97 107 112 ...
-- 93 96 101 98 109 95 94 99 80 74 99 85 76 117 124 ...
-- (Note; only fifteen are listed in each row to stay within
-- 70 columns.)
-- Chapter 33 - Program 3
-- This is a dynamic string package which can be used as an aid
-- in writing string intensive programs. Ada only has a static
-- string capability, so this package was written as an example of
-- how the Ada programming language can be expanded.
-- A dynamic string is defined as an array of characters of maximum
-- length of 255. The dynamic length of the dynamic string is
-- stored in the Max_Length field of the record. So the string
-- must be defined with a lower limit of 1 and an upper limit of
-- whatever the desired maximum length of the string is to be. The
-- subtype STRING_SIZE limits the string length when it is defined.
-- Put Outputs a dynamic string to the monitor
-- ConCat Concatenates two dynamic strings and puts the result
-- into a third dynamic string
-- Copy Copies a dynamic string to another dynamic string
-- Copy Copies a static string to a dynamic string
-- Delete Deletes a group of characters from a dynamic string
-- Insert Inserts a group of characters into a dynamic string
-- Length Returns the dynamic length of a dynamic string
-- Size_Of Returns the static length of a dynamic string
-- Pos Returns the first location of a dynamic string within
-- another dynamic string
with Ada.Text_IO; use Ada.Text_IO;
package DynStrng is
subtype STRING_SIZE is INTEGER range 0..255;
type STRING_ARRAY is array(STRING_SIZE range <>) of CHARACTER;
type DYNAMIC_STRING(Maximum_Length : STRING_SIZE) is
record
Dynamic_Length : INTEGER range 0..255;
String_Data : STRING_ARRAY(1..Maximum_Length);
end record;
-- Put : Display a dynamic string on the monitor.
procedure Put(Input_String : in DYNAMIC_STRING);
-- ConCat : Concatenation - The First_String is copied into the
-- Result_String, then the Second_String is copied
-- into the Result_String following the First_String.
-- If all characters fit, Result is set to TRUE.
-- If Result_String will not hold all characters,
-- only as many as will fit are copied and Result
-- is set to FALSE.
-- Result = TRUE, complete copy done.
-- Result = FALSE, some or all not copied
procedure ConCat(First_String : in DYNAMIC_STRING;
Second_String : in DYNAMIC_STRING;
Result_String : in out DYNAMIC_STRING;
Result : out BOOLEAN);
-- Copy : The String contained in Input_String is copied into
-- the string Output_String. This procedure is
-- overloaded to include copying from either dynamic
-- strings or static strings.
-- Result = TRUE, complete copy done
-- Result = FALSE, some or all not copied
procedure Copy(Input_String : in DYNAMIC_STRING;
Output_String : in out DYNAMIC_STRING;
Result : out BOOLEAN);
procedure Copy(Input_String : in STRING;
Output_String : out DYNAMIC_STRING;
Result : out BOOLEAN);
-- Delete : Beginning at First_Position, as many characters as are
-- indicated by Number_Of_Characters are deleted from
-- String_To_Modify. If there are not that many, only
-- as many as are left are deleted.
-- Result = TRUE, deletion was complete
-- Result = FALSE, only a partial deletion was done
procedure Delete(String_To_Modify : in out DYNAMIC_STRING;
First_Position : in STRING_SIZE;
Number_Of_Characters : in STRING_SIZE;
Result : out BOOLEAN);
-- Insert : The string String_To_Insert is inserted into the string
-- String_To_Modify begining at location Place_To_Insert
-- if there is enough room.
-- Result = TRUE, insert completed in full
-- Result = FALSE, not enough room for full insert
procedure Insert(String_To_Modify : in out DYNAMIC_STRING;
String_To_Insert : in DYNAMIC_STRING;
Place_To_Insert : in STRING_SIZE;
Result : out BOOLEAN);
-- Length : Returns the dynamic length of the string defined by
-- String_To_Measure.
function Length(String_To_Measure : in DYNAMIC_STRING)
return STRING_SIZE;
-- Size_Of : Returns the static length of the string, the actual
-- upper limit of the string definition.
function Size_Of(String_To_Measure : in DYNAMIC_STRING)
return STRING_SIZE;
-- Pos : Position of substring - The string String_To_Search is
-- searched for the string Required_String beginning
-- at Place_To_Start.
-- Result = TRUE, a search was possible
-- Result = FALSE, no search could be made
-- Location_Found = 0, no string found
-- Location_Found = N, start of matching string
procedure Pos(String_To_Search : in DYNAMIC_STRING;
Required_String : in DYNAMIC_STRING;
Place_To_Start : in STRING_SIZE;
Location_Found : out STRING_SIZE;
Result : out BOOLEAN);
end DynStrng;
package body DynStrng is
-- The display procedure overloads the existing
-- Put procedures to output a dynamic string. Note
-- that the existing Put is used in this new Put.
procedure Put(Input_String : in DYNAMIC_STRING) is
begin
for Index in 1..Input_String.Dynamic_Length loop
Put(Input_String.String_Data(Index));
end loop;
end Put;
procedure ConCat(First_String : in DYNAMIC_STRING;
Second_String : in DYNAMIC_STRING;
Result_String : in out DYNAMIC_STRING;
Result : out BOOLEAN) is
Intermediate_Result : BOOLEAN;
Character_Count : STRING_SIZE;
Room_Left : STRING_SIZE;
begin
-- Copy the first into the result string
Copy(First_String,Result_String,Intermediate_Result);
if Intermediate_Result then
Character_Count := Second_String.Dynamic_Length;
Room_Left := Result_String.String_Data'LAST
- Result_String.Dynamic_Length;
Result := TRUE;
if Character_Count > Room_Left then
Character_Count := Room_Left;
Result := FALSE;
end if;
for Index in 1..Character_Count loop
Result_String.String_Data
(Index + Result_String.Dynamic_Length) :=
Second_String.String_Data(Index);
end loop;
Result_String.Dynamic_Length :=
Result_String.Dynamic_Length + Character_Count;
else
Result := FALSE;
end if;
end ConCat;
-- This procedure overloads the name Copy to
-- copy from one dynamic string to another.
procedure Copy(Input_String : in DYNAMIC_STRING;
Output_String : in out DYNAMIC_STRING;
Result : out BOOLEAN) is
Character_Count : STRING_SIZE;
begin
-- First pick the smallest string size
Character_Count := Input_String.Dynamic_Length;
if Character_Count > Output_String.String_Data'LAST then
Character_Count := Output_String.String_Data'LAST;
Result := FALSE; -- The entire string didn't fit
else
Result := TRUE; -- The entire string fit
end if;
for Index in 1..Character_Count loop
Output_String.String_Data(Index) :=
Input_String.String_Data(Index);
end loop;
Output_String.Dynamic_Length := Character_Count;
end Copy;
-- This routine overloads the copy procedure name
-- to copy a static string into a dynamic string.
procedure Copy(Input_String : in STRING;
Output_String : out DYNAMIC_STRING;
Result : out BOOLEAN) is
Character_Count : STRING_SIZE;
begin
-- First pick the smallest string size
Character_Count := Input_String'LAST;
if Character_Count > Output_String.String_Data'LAST then
Character_Count := Output_String.String_Data'LAST;
Result := FALSE; -- The entire string didn't fit
else
Result := TRUE; -- The entire string fit
end if;
for Index in 1..Character_Count loop
Output_String.String_Data(Index) :=
Input_String(Index);
end loop;
Output_String.Dynamic_Length := Character_Count;
end Copy;
procedure Delete(String_To_Modify : in out DYNAMIC_STRING;
First_Position : in STRING_SIZE;
Number_Of_Characters : in STRING_SIZE;
Result : out BOOLEAN) is
Number_To_Delete : STRING_SIZE;
Number_To_Move : STRING_SIZE;
Last_Dynamic_Character : STRING_SIZE :=
String_To_Modify.Dynamic_Length;
begin
-- can we delete any at all?
if First_Position > Last_Dynamic_Character then
Result := FALSE;
return;
end if;
-- Decide how many to delete
if (First_Position + Number_Of_Characters)
> Last_Dynamic_Character then
Number_To_Delete := Last_Dynamic_Character
- First_Position + 1;
Result := FALSE;
else
Number_To_Delete := Number_Of_Characters;
Result := TRUE;
end if;
-- find out how many to move back
if (Last_Dynamic_Character - (First_Position + Number_To_Delete))
>= 0 then
Number_To_Move := 1 + Last_Dynamic_Character
- (First_Position + Number_To_Delete);
else
Number_To_Move := 0;
end if;
-- now delete the characters by moving them back
for Index in First_Position..
(First_Position + Number_To_Move - 1) loop
String_To_Modify.String_Data(Index) :=
String_To_Modify.String_Data(Index + Number_To_Delete);
end loop;
String_To_Modify.Dynamic_Length :=
String_To_Modify.Dynamic_Length - Number_To_Delete;
end Delete;
procedure Insert(String_To_Modify : in out DYNAMIC_STRING;
String_To_Insert : in DYNAMIC_STRING;
Place_To_Insert : in STRING_SIZE;
Result : out BOOLEAN) is
Number_To_Add : STRING_SIZE;
Number_To_Move : STRING_SIZE;
Room_Left : STRING_SIZE;
begin
-- Can we add any at all?
if (Place_To_Insert > String_To_Modify.String_Data'LAST) or
(Place_To_Insert > String_To_Modify.Dynamic_Length + 1) then
Result := FALSE;
return;
end if;
Result := TRUE;
-- How many can we add?
Number_To_Add := String_To_Modify.String_Data'LAST
- Place_To_Insert + 1;
if Number_To_Add > String_To_Insert.Dynamic_Length then
Number_To_Add := String_To_Insert.Dynamic_Length;
end if;
-- Find how many to move forward
Number_To_Move := String_To_Modify.Dynamic_Length
- Place_To_Insert + 1;
Room_Left := String_To_Modify.String_Data'LAST
- Place_To_Insert + 1;
if Room_Left < Number_To_Move then
Number_To_Move := Room_Left;
end if;
-- Move them forward
for Index in reverse Place_To_Insert..Place_To_Insert
+ Number_To_Move - 1 loop
String_To_Modify.String_Data(Index + Number_To_Add) :=
String_To_Modify.String_Data(Index);
end loop;
for Index in 1..Number_To_Add loop
String_To_Modify.String_Data(Index + Place_To_Insert - 1) :=
String_To_Insert.String_Data(Index);
end loop;
-- Increase the length of the string
String_To_Modify.Dynamic_Length :=
String_To_Modify.Dynamic_Length + Number_To_Add;
if String_To_Modify.Dynamic_Length >
String_To_Modify.String_Data'LAST then
String_To_Modify.Dynamic_Length := String_To_Modify.String_Data'LAST;
end if;
end Insert;
-- This returns the dynamic length of a string
function Length(String_To_Measure : in DYNAMIC_STRING)
return STRING_SIZE is
begin
return String_To_Measure.Dynamic_Length;
end Length;
-- This returns the static length of a string
function Size_Of(String_To_Measure : in DYNAMIC_STRING)
return STRING_SIZE is
begin
return String_To_Measure.String_Data'LAST;
end Size_Of;
procedure Pos(String_To_Search : in DYNAMIC_STRING;
Required_String : in DYNAMIC_STRING;
Place_To_Start : in STRING_SIZE;
Location_Found : out STRING_SIZE;
Result : out BOOLEAN) is
End_Search : STRING_SIZE;
Characters_All_Compare : BOOLEAN;
begin
Location_Found := 0;
-- can we search the string at all?
if (Place_To_Start < String_To_Search.Dynamic_Length) and
(Place_To_Start < String_To_Search.String_Data'LAST) then
Result := TRUE;
else
Result := FALSE;
return;
end if;
-- search the loop for the string now
End_Search := String_To_Search.Dynamic_Length -
Required_String.Dynamic_Length + 1;
for Index in Place_To_Start..End_Search loop -- search loop
Characters_All_Compare := TRUE;
for Count in 1..Required_String.Dynamic_Length loop
if Required_String.String_Data(Count) /=
String_To_Search.String_Data(Count + Index - 1) then
Characters_All_Compare := FALSE;
exit; -- exit loop, a character did not match
end if;
end loop;
if Characters_All_Compare then
Location_Found := Index;
return; -- string match found, return location
end if;
end loop; -- end search loop
end Pos;
end DynStrng;
-- Chapter 33 - Program 4
with Ada.Text_IO; use Ada.Text_IO;
with DynStrng; use DynStrng;
procedure TestStrn is
package Int_IO is new Ada.Text_IO.Integer_IO(INTEGER);
use Int_IO;
Stuff : DYNAMIC_STRING(35);
Result : BOOLEAN;
begin
Copy("ABCDEFGHIJKL$", Stuff, Result);
Put(Size_Of(Stuff), 4);
Put(Length(Stuff), 4);
Put(Stuff);
New_Line;
Copy("ABCD$", Stuff, Result);
Put(Size_Of(Stuff), 4);
Put(Length(Stuff), 4);
Put(Stuff);
New_Line;
Copy("", Stuff, Result);
Put(Size_Of(Stuff), 4);
Put(Length(Stuff), 4);
Put(Stuff);
New_Line;
end TestStrn;
-- Chapter 33 - Program 5
-- This program will calculate the number of days old you are.
-- It is a rather dumb program, but illustrates some interesting
-- programming techniques. It checks all input to see that they
-- are in the correct range before continuing. Since the number
-- of days can easily exceed the limits of type INTEGER, and we
-- cannot count on LONG_INTEGER being available, a fixed point
-- variable is used for the total number of days since Jan 1, 1880.
-- This program also passes a record to a procedure, where it is
-- modified and returned.
-- This is a repeat of the program named AGE.ADA from chapter 16
-- of this tutorial. This program uses the CALENDAR package for
-- the current date so the user does not have to enter today's
-- date. It also uses some of the subtypes from the CALENDAR
-- package, but not the YEAR_NUMBER, because it does not follow
-- our desired range.
with Ada.Text_IO, Calendar;
use Ada.Text_IO, Calendar;
procedure Age2 is
LOW_YEAR : constant := 1880;
MAX : constant := 365.0 * (2100 - LOW_YEAR);
type AGES is delta 1.0 range -MAX..MAX;
Days_Since_1880 : AGES;
Present_Age : AGES;
Today : TIME; -- Present date and time
This_Month : MONTH_NUMBER;
This_Day : DAY_NUMBER;
This_Year : INTEGER range LOW_YEAR..2100;
Seconds : DAY_DURATION;
type DATE is
record
Month : MONTH_NUMBER;
Day : DAY_NUMBER;
Year : INTEGER range LOW_YEAR..2100;
Days : AGES;
end record;
Birth_Day : DATE;
package Int_IO is new Ada.Text_IO.Integer_IO(INTEGER);
use Int_IO;
package Fix_IO is new Ada.Text_IO.Fixed_IO(AGES);
use Fix_IO;
procedure Get_Date(Date_To_Get : in out DATE) is
Temp : INTEGER;
begin
Put(" month --> ");
loop
Get(Temp);
if Temp in 1..12 then
Date_To_Get.Month := Temp;
exit; -- month OK
else
Put_Line(" Month must be in the range of 1 to 12");
Put(" ");
Put(" month --> ");
end if;
end loop;
Put(" ");
Put(" day ----> ");
loop
Get(Temp);
if Temp in 1..31 then
Date_To_Get.Day := Temp;
exit; -- day OK
else
Put_Line(" Day must be in the range of 1 to 31");
Put(" ");
Put(" day ----> ");
end if;
end loop;
Put(" ");
Put(" year ---> ");
loop
Get(Temp);
if Temp in LOW_YEAR..2100 then
Date_To_Get.Year := Temp;
exit; -- year OK
else
Put_Line(" Year must be in the range of 1880 to 2100");
Put(" ");
Put(" year ---> ");
end if;
end loop;
Date_To_Get.Days := 365 * AGES(Date_To_Get.Year - LOW_YEAR)
+ AGES(31 * Date_To_Get.Month + Date_To_Get.Day);
end Get_Date;
begin
-- Get todays date
Today := Clock;
Split(Today, This_Year, This_Month, This_Day, Seconds);
Days_Since_1880 := 365 * AGES(This_Year - LOW_YEAR)
+ AGES(31 * This_Month + This_Day);
Put("Enter your birthday;");
Get_Date(Birth_Day);
New_Line(2);
Present_Age := Days_since_1880 - Birth_Day.Days;
if Present_Age < 0.0 then
Put("You will be born in ");
Present_Age := abs(Present_Age);
Put(Present_Age,6,0,0);
Put_Line(" days.");
elsif Present_Age = 0.0 then
Put_Line("Happy birthday, you were just born today.");
else
Put("You are now ");
Put(Present_Age,6,0,0);
Put_Line(" days old.");
end if;
end Age2;
-- Chapter 33 - Program 6
package One_Man is
type AVAILABILITY is (AVAILABLE, IN_USE);
Fork_Usage : array(1..5) of AVAILABILITY;
type ACTIVITY is (THINKING, HAS_LEFT_FORK, HAS_BOTH_FORKS);
Philosopher_Activity : array(1..5) of ACTIVITY;
task type EATING_OR_THINKING is
entry Start(Left_Fork, Right_Fork : INTEGER);
end EATING_OR_THINKING;
end One_Man;
with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Calendar, Random;
use Ada.Text_IO, Ada.Integer_Text_IO;
package body One_Man is
package My_Random is new Random(FLOAT);
use My_Random;
procedure Get_Fork(Identifier : INTEGER;
Left_Or_Right : INTEGER) is
begin
Fork_Usage(Identifier) := IN_USE;
end Get_Fork;
procedure Return_Fork(Identifier : INTEGER) is
begin
Fork_Usage(Identifier) := AVAILABLE;
end Return_Fork;
task body EATING_OR_THINKING is
Left, Right : INTEGER;
Ident : INTEGER renames Left;
begin
accept Start(Left_Fork, Right_Fork : INTEGER) do
Left := Left_Fork;
Right := Right_Fork;
Philosopher_Activity(Ident) := THINKING;
end Start;
loop
Put("Philosopher");
Put(Ident, 2);
Put_Line(" is thinking.");
delay Calendar.DAY_DURATION(Random_Number);
loop
delay 0.10;
exit when Fork_Usage(Left) = AVAILABLE;
end loop;
Get_Fork(Ident, Left);
Philosopher_Activity(Ident) := HAS_LEFT_FORK;
Put("Philosopher");
Put(Ident, 2);
Put_Line(" has his left fork");
delay Calendar.DAY_DURATION(Random_Number);
loop
delay 0.10;
exit when Fork_Usage(Right) = AVAILABLE;
end loop;
Get_Fork(Ident,Right);
Philosopher_Activity(Ident) := HAS_BOTH_FORKS;
Put("Philosopher");
Put(Ident, 2);
Put_Line(" has his right fork and is eating");
delay Calendar.DAY_DURATION(Random_Number);
Return_Fork(Left);
Return_Fork(Right);
Philosopher_Activity(Ident) := THINKING;
end loop;
end EATING_OR_THINKING;
begin
Set_Seed; -- Initialize the random number generator
for Index in 1.. 5 loop
Fork_Usage(Index) := AVAILABLE;
Philosopher_Activity(Index) := THINKING;
end loop;
end One_Man;
with Ada.Text_IO, One_Man;
use Ada.Text_IO, One_Man;
procedure Philos is
-- Declare all 5 tasks
Philosopher_1 : One_Man.EATING_OR_THINKING;
Philosopher_2 : One_Man.EATING_OR_THINKING;
Philosopher_3 : One_Man.EATING_OR_THINKING;
Philosopher_4 : One_Man.EATING_OR_THINKING;
Philosopher_5 : One_Man.EATING_OR_THINKING;
begin
-- Assign forks to Philosophers & start
Philosopher_1.Start(Left_Fork => 1, Right_Fork => 2);
Philosopher_2.Start(Left_Fork => 2, Right_Fork => 3);
Philosopher_3.Start(Left_Fork => 3, Right_Fork => 4);
Philosopher_4.Start(Left_Fork => 4, Right_Fork => 5);
Philosopher_5.Start(Left_Fork => 5, Right_Fork => 1);
loop -- Watch for deadlock to occur
delay 0.01;
if Philosopher_Activity(1) = HAS_LEFT_FORK and
Philosopher_Activity(2) = HAS_LEFT_FORK and
Philosopher_Activity(3) = HAS_LEFT_FORK and
Philosopher_Activity(4) = HAS_LEFT_FORK and
Philosopher_Activity(5) = HAS_LEFT_FORK then exit;
end if;
end loop;
Put_Line("Deadlock detected, program operation aborted.");
abort Philosopher_1, Philosopher_2, Philosopher_3,
Philosopher_4, Philosopher_5;
end Philos;
-- Chapter 33 - Program 7
generic
type ITEM is private;
package GenStack is
procedure Push(In_Item : in ITEM); -- In_Item is added to the
-- stack if there is room.
procedure Pop(Out_Item : out ITEM); -- Out_Item is removed from
-- stack and returned if a
-- character is on stack.
-- else a blank is returned
function Is_Empty return BOOLEAN; -- TRUE if stack is empty
function Is_Full return BOOLEAN; -- TRUE if stack is full
function Current_Stack_Size return INTEGER;
procedure Clear_Stack; -- Reset the stack to empty
end GenStack;
package body GenStack is
Maximum_Size : constant := 25;
type ITEM_ARRAY is array(1..Maximum_Size) of ITEM;
Stack_List : ITEM_ARRAY; -- The stack itself, purposely
-- defined very small.
Top_Of_Stack : INTEGER := 0; -- This will always point to
-- the top entry on the stack.
procedure Push(In_Item : in ITEM) is
begin
if not Is_Full then
Top_Of_Stack := Top_Of_Stack + 1;
Stack_List(Top_Of_Stack) := In_Item;
end if;
end Push;
procedure Pop(Out_Item : out ITEM) is
begin
if Is_Empty then
null; -- Nothing to return
else
Out_Item := Stack_List(Top_Of_Stack);
Top_Of_Stack := Top_Of_Stack - 1;
end if;
end Pop;
function Is_Empty return BOOLEAN is
begin
return Top_Of_Stack = 0;
end Is_Empty;
function Is_Full return BOOLEAN is
begin
return Top_Of_Stack = Maximum_Size;
end Is_Full;
function Current_Stack_Size return INTEGER is
begin
return Top_Of_Stack;
end Current_Stack_Size;
procedure Clear_Stack is
begin
Top_Of_Stack := 0;
end Clear_Stack;
end GenStack;
-- Chapter 33 - Program 8
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
with GenStack;
procedure TryStak is
package Char_Stack is new GenStack(CHARACTER);
use Char_Stack;
Example : constant STRING := "This is the first test.";
Another : constant STRING :=
"This is another test and this should not fit.";
procedure Fill_The_Stack(Input_Line : STRING) is
begin
Clear_Stack;
for Index in 1..Input_Line'LAST loop
if Is_Full then
Put_Line("The stack is full, no more added.");
exit;
else
Push(Input_Line(Index));
end if;
end loop;
end Fill_The_Stack;
procedure Empty_The_Stack is
Char : CHARACTER;
begin
loop
if Is_Empty then
New_Line;
Put_Line("The stack is empty.");
exit;
else
Pop(Char);
Put(Char);
end if;
end loop;
end Empty_The_Stack;
begin
Put_Line(Example);
Fill_The_Stack(Example);
Empty_The_Stack;
New_Line;
Put_Line(Another);
Fill_The_Stack(Another);
Empty_The_Stack;
end TryStak;
-- Result of execution
-- This is the first test.
-- .tset tsrif eht si sihT
-- The stack is empty.
--
-- This is another test and this should not fit.
-- The stack is full, no more added.
-- dna tset rehtona si sihT
-- The stack is empty.

You might also like