After feeling a bit overwhelmed with Ada for Software Engineers, I decided to take a break and try to write some simple programs so that I can get more familiar with the core Ada language. Hopefully these entries will help any one else interested in Ada.
The following is a sample program I created to become accustomed with the some basics of Ada: console I/O, arrays, and most importantly, strings.
with Ada.Text_IO; with Ada.Strings; with Ada.Characters.Latin_1; use Ada.Text_IO; procedure Reverser is procedure ReverseIt ( Text: in String; Len: in Integer ) is begin for Index in reverse Text'First .. Len loop Put(Text(Index)); end loop; Put(Ada.Characters.Latin_1.CR); Put(Ada.Characters.Latin_1.LF); end ReverseIt; Input: String(1..255); Last: Integer; begin loop Put(">> "); Get_Line(Input, Last); exit when Input(1) = Ada.Characters.Latin_1.ESC; ReverseIt(Input, Last); end loop; end Reverser;
First of all, Ada is case insensitive, but good Ada style dictates that we name variables and functions with upper-case letters. I have yet to find out whether camel case (ReverseIt) or underbars (Reverse_It) are preferred for nameing variables and functions that have mulitple words. Before I forget, I should also mention that array indeces start from one is Ada, not zero like in C++.
Ada is quite verbose and easy to read even if you don’t know the syntax. Without know the details of the syntax, a developer familiar with basic procedural programming should have no trouble understanding what this simple program does. It simply reads a string from the user and prints it out reversed on the string.
For a C++ programmer, the syntax is quiet different though. In the definition of the ReverseIt procedure, each parameter in the parameter list is labed as in before the type is listed. “Len: in Integer” is equivalent to “const int Len” in C++. For ouputs we can use out, which would be similar to “int &Len” and for parameters that function as both inputs and outputs, we can use inout.
The with clauses are like #include statements in C++ in that they allow our program to call functions from external libraries. Ada.Text_IO and Ada.String libraries are similar to cstdio and cstring respectively. Ada.String is for fixed length strings like C-style character arrays. The use clause is similar to the C++ namespace statement as it brings the library into scope and allows us to leave off the library name when calling a function from the library. Instead of using Ada.Text_IO.Get_Line in the program we can simply use Get_Line. It might also be a good idea to take a look at the Ada.Text_IO library to better understand how to use the I/O functions.
The final thing to be wary of is semicolons in the parameter list for the ReverseIt procedure. The last parameter for the procedure does NOT have a semicolon. If you place a semicolon at the end of the statement, GNAT will become angry with you. My advice is to always complete the parenthesis first and then go back and fill in the parameters as necessary.
Other oddities for the C++ programmer might be that both local variables and procedures/functions are declared before the main program body is defined. Also, the Ada.Characters.Latin_1 library is withed so that we use control characters like carriage return (CR), line-feed (LF) and escape (ESC). These are the equivalent of escape characters such as “\n” or “\r” in C++. If you look at the .ads file for the Latin_1 library, you can see it simply defines a bunch of character constansts with the proper ASCII values.
Finally, note that there is no main function defined in the code. Unlike the C++ run-time, the Ada run-time does not require the entry point of the program to be named “main”. However, the entry point procedure should be the same as the file name. In this case, the entry point is the Reverser procedure, which is why the file is named reverser.adb.
To build this program, first copy the program code into your favorite editor and save it as reverser.adb. GNAT expects program bodies to end in the suffix .adb. It expects specifications–like C++ header files–to end in the .ads suffix. For now lets not worry about specifications as our programs will be simple in the beginning. We can either run the single-step build process, or we can build it separate steps. Execute “gnatmake reverser.adb” on the command line to runt he single-step process. If there are no errors, the reverser binary is created in your working directory.
For learning purposes though, lets build the program step-by-step. To start, execute the following on the command line:
gnatmake -c reverser.adb
If you look in your working directory, reverser.o and reverser.ali have been created. On the screen you probably noticed that the above command actually invoked “gcc -c reverser.adb”. The GNU compiler supports not only C/C++, but also languages like Objective-C and Ada. Just as in C++, after compiling the program, an object file, reverser.o, is created. The GNAT manual explains the Ada Library Info (ALI) file in more depth, but it essentially holds information about required libraries and version information.
The next step is to bind the program. The binding process uses information from ALI and object files to make sure that package (modules) versions match and that there are no inconsistencies. There is no equivalent in C++, and binding is touted by Ada developers as yet another way that Ada is inherently safer than C/C++ programs. To bind the application, execute the following:
We do not need to specify the ALI file with its suffix because the binder figures it out. The bind process produces two files, b~reverser.ads and b~reverser.adb. Take a look at the contents of the files–quite a bit of information in there! Refer to the GNAT manual if you want to know more, but lets move on and worry about the details later.
The final step is to link the program with required libraries to produce an executable (just like we do in C++).
Take a look at the working directory. Notice that the gnatbind output is now removed and that there is also an executable for our program.
So that is it for our first program. If the program doesn’t make any sense, spend some time looking at the library specifications. For further enrichment, try changing things in the program such as outputing past the length of the input string or passing bad arguments.