Previous | Next | Table of Contents | Index | Program List | Copyright

2.11 Tricks of the Trade: Common Programming Errors

One of the first things you will discover in writing programs is that a program often does not run correctly the first time that it is submitted. Murphy`s law, "If something can go wrong, it will," seems to have been written with computer programming in mind. In fact, errors are so common that they have their own special name--bugs--and the process of correcting them is called debugging a program. To alert you to potential problems, a section on common errors appears near the end of many chapters of this book.

There are three basic categories of errors:

Compilation Errors

Program 2.10 is a modified version of the distance program, Program 2.5. The modified program contains errors, which we have purposely put in for illustrative purposes. First, we ended the first PROCEDURE line with a semicolon instead of IS, and second, we garbled the assignment statement that computes the distance, pretending that we didn't know an assignment symbol is := and that the result variable must be on the left side of the assignment. We inserted several other errors as well. We declared How_Fast as Float and also used Ada.Float_Text_IO calls instead of Ada.Integer_Text_IO calls. These are all errors commonly made by beginners.

Program 2.10
Distance Program with Intentional Errors

WITH Ada.Text_IO;
WITH Ada.Float_Text_IO;
PROCEDURE Distance_with_Errors;
------------------------------------------------------------------------
--| Finds distance traveled, given travel time and average speed  
--| Author: Michael B. Feldman, The George Washington University 
--| Last Modified: July 1995                                     
------------------------------------------------------------------------
 
  How_Long : Natural;
  How_Fast : Float;      
  How_Far  : Natural;
 
BEGIN -- Distance_with_Errors

  -- prompt user for hours and average speed
  Ada.Text_IO.Put 
    (Item => "How many hours will you be driving (integer) ? ");
  Ada.Float_Text_IO.Get (Item => How_Long);
  Ada.Text_IO.Put 
    (Item => "At what average speed (miles per hour, integer) ? ");
  Ada.Float_Text_IO.Get (Item => How_Fast);
  
  -- compute distance driven
  How_Fast * How_long = How_Far
   
  -- display results
  Ada.Text_IO.Put (Item => "You will travel about ");
  Ada.Float_Text_IO.Put (Item => How_Far);
  Ada.Text_IO.Put (Item => " miles");
  Ada.Text_IO.New_Line;

END Distance_with_Errors;
Figure 2.7 shows a compilation listing of the driving-distance program ( Program 2.10) produced by the GNAT compiler on a Sun SPARC system. You can ask the compiler to create a listing during translation. The listing shows each line of the source program (preceded by its line number) and also displays any errors detected by the compiler. You can tell from reading the listing just where the compiler found errors.

Figure 2.7
Compilation Listing with Error Messages

NYU GNAT Compiler Version 2.07 (C) Copyright NYU, 1992,1993,1994,1995

Compiling: distance_with_errors.adb  last modified at 95-12-11 13:53.51 GMT.

 1. WITH Ada.Text_IO;
 2. WITH Ada.Float_Text_IO;
 3. PROCEDURE Distance_with_Errors ;
                                   |
    >>> ";" should be "IS"

 4. ----------------------------------------------------------------
 5. --| Finds distance traveled, given travel time and average speed
 6. --| Author: Michael B. Feldman, The George Washington University
 7. --| Last Modified: July 1995
 8. ----------------------------------------------------------------
 9.   How_Long : Natural;
10.   How_Fast : Float;
11.   How_Far  : Natural;
12.
13. BEGIN -- Distance_with_Errors
14.
15.   -- prompt user for hours and average speed
16.   Ada.Text_IO.Put
17.     (Item => "How many hours will you be driving (integer) ? ");
18.   Ada.Float_Text_IO.Get (Item => How_Long);
19.   Ada.Text_IO.Put
20.     (Item => "At what average speed (miles per hour, integer) ? ");
21.   Ada.Float_Text_IO.Get (Item => How_Fast);
22.
23.   -- compute distance driven
24.   How_Fast * How_long = How_Far
              |
    >>> missing ":="

25.
26.   -- display results
27.   Ada.Text_IO.Put (Item => "You will travel about ");
28.   Ada.Float_Text_IO.Put (Item => How_Far);
29.   Ada.Text_IO.Put (Item => " miles");
30.   Ada.Text_IO.New_Line;
31.
32. END Distance_with_Errors;

 32 lines:

The actual format of the listing and error messages produced by your compiler may differ from Fig. 2.7. In this listing whenever an error is detected, the compiler prints a line starting with >>>. A vertical line (|) points to the position in the preceding line where the error was detected. This is usually, but not always, where the error occurred. The error is explained on the next line.

To see how a compiler listing works, look at the first error reported. The compiler caught the misplaced semicolon and also the garbled assignment statement but did not notice the use of the wrong input/output package, or the improperly declared variable.

This is because this particular compiler first finds all errors in use of the syntax or grammar, that is, the punctuation and spelling rules. Often if there are grammatical errors, the compiler goes no further.

Syntactic and Semantic Errors

Suppose we corrected the two syntax errors, then recompiled the program. Figure 2.8 shows the new listing.

Figure 2.8
Another Compilation Listing with Error Messages

NYU GNAT Compiler Version 2.07 (C) Copyright NYU, 1992,1993,1994,1995

Compiling: distance_with_errors.adb  last modified at 95-12-11 14:01.02 GMT.

 1. WITH Ada.Text_IO;
 2. WITH Ada.Float_Text_IO;
 3. PROCEDURE Distance_with_Errors IS
 4. ------------------------------------------------------------------
 5. --| Finds distance traveled, given travel time and average speed
 6. --| Author: Michael B. Feldman, The George Washington University
 7. --| Last Modified: July 1995
 8. ------------------------------------------------------------------
 9.   How_Long : Natural;
10.   How_Fast : Float;
11.   How_Far  : Natural;
12.
13. BEGIN -- Distance_with_Errors
14.
15.   -- prompt user for hours and average speed
16.   Ada.Text_IO.Put
17.     (Item => "How many hours will you be driving (integer) ? ");
18.   Ada.Float_Text_IO.Get (Item => How_Long);
                       |
    >>> invalid parameter list in call

19.   Ada.Text_IO.Put
20.     (Item => "At what average speed (miles per hour, integer) ? ");
21.   Ada.Float_Text_IO.Get (Item => How_Fast);
22.
23.   -- compute distance driven
24.   How_Far := How_Fast * How_long;
                          |
    >>> invalid operand types for operator "*"

25.
26.   -- display results
27.   Ada.Text_IO.Put (Item => "You will travel about ");
28.   Ada.Float_Text_IO.Put (Item => How_Far);
                       |
    >>> invalid parameter list in call
    >>> possible missing instantiation of Text_IO.Integer_IO

29.   Ada.Text_IO.Put (Item => " miles");
30.   Ada.Text_IO.New_Line;
31.
32. END Distance_with_Errors;

 32 lines:

The error messages in lines 3 and 24 did not re-appear because we corrected those errors. This time, the compiler caught the remaining errors, which are not grammatical but semantic or usage errors. The first message, at line 18, informs us that the Item parameter to Get is invalid, in this case because we tried to use the floating-point Get to read into an Integer variable. The same message appears at line 24, for the same reason. Finally, the message at line 28 indicates that the multiplication is invalid, because we are trying to multiply a Float variable (the incorrectly declared How_Fast) by a Natural one (How_Long). As we learned in Section 2.10, such mixing of integer and floating-point values is not allowed.

It is often hard to interpret error messages, but it gets easier as your experience increases. Also, sometimes you have made a certain error but the compiler interprets it as something entirely different. Remember, the compiler is only a computer program and is not as good a detective as you are!

Propagation Errors

One compilation error often leads to the generation of a number of error messages. (These "extra" errors are often called propagation errors.) For this reason, it is often a good idea to concentrate first on correcting the errors in the declaration part of a program and then to recompile, rather than to attempt to fix all the errors at once.

Many later errors will disappear once the declarations are correct. An undefined variable error occurs if the compiler cannot find the declaration for an identifier referenced in the program body. This can happen because you omitted the declaration or misspelled the identifier. It can also happen if you forget to supply a WITH for a package needed by the program.

Programmers who are just learning to write Ada programs often forget to write IS and to insert semicolons in the required places. These are two of the most common compilation errors made by beginners, and unfortunately they are also errors which sometimes confuse the compiler hopelessly. Leaving out an IS or a semicolon, or using one where the other is expected, can often lead to an entire sequence of messages (propagation errors), which will all disappear when the original error is corrected. A word to the wise is sufficient!

Another common Ada compilation error is the confusion of single and double quotation marks. You must remember that a string literal in Ada is surrounded by double quotation marks, but a single character literal must be surrounded by single quotation marks.

As we have seen, compilation errors are generally of two kinds: syntax errors and semantic errors. Some Ada compilers distinguish between these in their error messages; others do not. Some compilers try to find both syntactic and semantic errors at the same time; others do it in two stages, as we saw here.

In languages like Ada with data types, semantic errors occur quite frequently. One of the things you will need to be careful about is making sure that the types of your variables match the expectation of the expression or procedure in which the variables are used. If a procedure expects an Integer variable, supplying a Float variable won't do; also, you cannot mix Integer and Float variables in the same expression!

Finally, there is no need to panic at getting a long list of messages; it happens to all new programmmers and occasionally to experienced ones as well. There are probably only a few actual errors and many extra messages because of propagation, or because you repeated the error in several places, so it's best just to try to correct the first one or two errors, then recompile. You'll be amazed at how quickly the number of messages decreases.

Run-Time Errors

As discussed above, run-time errors are called exceptions in Ada. The most common exceptions encountered by beginners are those relating to the ranges of variables in their programs. A range error occurs when a program tries to save an inappropriate value in a variable. This can happen in one of two ways: Either the program itself computes a result that is out of range for the variable in which it will be saved, or the program user enters an out-of-range value from the keyboard. Ada gives the name Constraint_Error to such a range error; Ada uses the term raising an exception for reporting the occurrence of such a runtime error.

As an example of the second case, consider case 5 of the test plan for the coin collection program ( Program 2.9, Table 2.5). Figure 2.9 shows a sample run in which we enter a negative value for the number of pennies. Recall that the variable Pennies was declared to be of type Natural, that is, nonnegative.

Figure 2.9
Sample Run of Coin_Collection, Negative Input Entered

How many nickels do you have? 13
How many pennies do you have? -5

raised Constraint_Error

Trace Back Information
    Program Name                File Name                       Line
    ------------                ---------                       ----
    coin_collection             coin_collection.adb             17
The form of the exception report and "trace back" to your source program varies from compiler to compiler, but the content is the same: You are told which exception was raised and where.

Figure 2.10 shows the results of test case 6, in which "bad" input is entered, namely, a sequence of characters that cannot be an integer token.

Figure 2.10
Sample Run of Coin_Collection, Noninteger Input Entered

How many nickels do you have? xyz

raised unhandled exception

***Exception ada.io_exceptions.data_error Raised

Trace Back Information
    Program Name                  File Name                       Line
    ------------                  ---------                       ----
    coin_collection               coin_collection.adb             15
In this case, the exception raised is an input/output exception called Data_Error. This exception is raised when a Get operation gets a token of the wrong form, in this case a string of letters instead of an integer. The difference between Constraint_Error and Data_Error is that in the former case the value is formed correctly but is too large or too small, while in the latter case the token is not formed properly.

To summarize, Ada's data types and exception system are designed to help you write programs whose results will make sense. In this book we will pay very careful attention to this matter, because it is important and can be very useful to you.

Debugging a program can be time-consuming. The best approach is to plan your programs carefully and desk check them beforehand to eliminate bugs before they occur. If you are not sure of the syntax for a particular statement, look it up in the syntax displays in the text or in Appendix B. Also, take care that your program variables have types that are appropriate and sensible. If you follow this approach, you will save yourself much time and trouble.


Previous | Next | Table of Contents | Index | Program List | Copyright

Copyright © 1996 by Addison-Wesley Publishing Company, Inc.