Das Programm 'Hello proggen.org'

  • Der Aufbau einer Quellcodedatei
  • Datentypen und Funktionen
  • Die Funktion main

Der Quelltext

Hier für den Anfang ein Hello World Programm, das auf der Kommandozeile ein „Hello proggen.org!“ ausgibt:

with Ada.Text_IO;
use Ada.Text_IO;
 
procedure Hello is
   Greeting : String := "Hello proggen.org!";
begin
   Put_Line(Greeting); -- Alternativ: Ada.Text_IO.Put_Line(Greeting);
end Hello;

Der Aufbau einer Quellcodedatei

Bei Ada wird der Quellcode in sogenannte „Compilation Units“ („Kompilationseinheiten“) unterteilt. Es gibt Compiler, die mehrere solche Einheiten in einer Datei zulassen, GNAT erwartet jedoch jede Einheit in einer eigenen Datei. Falls man doch alles in eine Datei schreiben möchte, kann man das Tool gnatchop verwenden, um die Einheiten für den Compiler aufzuspalten.

Context Clause

Zu Beginn einer Compilation Unit steht die „Context Clause“, die entweder leer ist oder with- und use-Statements enthält. Ein with gibt an, dass der Quellcode von anderen Bibliotheken abhängig ist. Im obigen Fall benötigen wir with Ada.Text_IO;, um die Text-Input-/Output-Methoden zu benutzen. Das use Ada.Text_IO; importiert die öffentlichen Methoden und Variablen aus Ada.Text_IO in den lokalen Namensraum. Dies dient nur als Beispiel, das Importieren auf oberster Ebene sollte ähnlich dem using namespace std; bei C++ gut überdacht werden.

Library Item

Nach der Context Clause folgt das eigentliche „Library Item“, in diesem Fall eine Prozedur. Bei Ada wird zwischen Prozeduren und Funktionen unterschieden. Letztere geben einen Wert zurück und können nur an Stellen benutzt werden, an denen ein Wert des Typs erwartet wird, zum Beispiel auf der rechten Seite einer Zuweisung.

Weitere Arten von Library Items wären Packages, Generics oder Renamings.

Das Schlüsselwort is nach dem Prozedur-Namen leitet die Implementation der Prozedur ein. Eine reine Deklaration würde nach dem Namen (sowie eventueller Parameter) enden. Danach folgt der Deklarations-Teil, das Schlüsselwort begin, eine Folge von Anweisungen, sowie das Schlüsselwort end. Der Name der Prozedur am Ende ist optional, hilft aber dabei Flüchtigkeitsfehler zu erkennen, da ein falscher Name nicht akzeptiert wird.

Deklarationen

Im Deklarations-Teil wird die Variable Greeting deklariert. Solch eine Deklaration läuft nach dem Schema Name : Typ; ab. Es kann auch wie im Beispiel eine Initialisierung mitgegeben werden: Name : Typ := Wert;. Das := steht in Ada für den Zuweisungs-Operator.

Anweisungen

Nach dem begin folgen die Anweisungen. Im oberen Beispiel ist dies lediglich eine. Es wird die Prozedur Put_Line aufgerufen, die in der Bibliothek Ada.Text_IO definiert ist. Durch das use in der Context Clause kann sie direkt verwendet werden. Andernfalls müsste der komplette Name benutzt werden: Ada.Text_IO.Put_Line.

Wenn eine Prozedur oder Funktion Parameter erwartet, kommen nach dem Namen die Parameter, durch Kommata getrennt und in runden Klammern. Falls keine Parameter erwartet werden, oder die Parameter einen Default-Wert besitzen und diese benutzt werden sollen, müssen auch die runden Klammern weggelassen werden. Eine solcher Prozedur-Aufruf wäre z.B.: New_Line;

Da bei Ada nicht zwischen Groß- und Kleinschreibung unterschieden wird, könnte die Zeile auch so aussehen:

put_LINE(GREEting);

Allerdings wird zur besseren Lesbarkeit meistens Camel_Case für Packages, Variablen, Prozeduren und Funktionen, sowie Kleinschreibung für Schlüsselwörter benutzt.

Kommentare

Bei Ada gibt es nur Zeilen-Kommentare. Diese beginnen mit einem doppelten Minus und gehen bis zum Ende der Zeile.

Hauptprogramm

Das Hauptprogramm (bei C main()) besitzt keinen fest definierten Namen, es muss lediglich eine Prozedur auf oberster Ebene (also nicht innerhalb eines Packages) sein.

Kompilieren des Programms

GNAT erwartet üblicherweise den Namen des Library Items als Dateiname in Kleinschreibung (bei Betriebssystemen ohne Unterscheidung von Groß- und Kleinschreibung bei Dateinamen ist es prinzipiell egal, kann dann aber bei anderen Systemen zu Problemen führen). Für die Deklarationen (auch „Specification“ genannt, vergleichbar mit Header bei C) erwartet GNAT die Erweiterung .ads, und für die Implementierung („Body“) die Erweiterung .adb. Für unser Beispiel wäre der Dateiname also hello.adb.

Da dies ein einfaches Beispiel ist, können wir das Beispiel folgendermaßen kompilieren, wenn wir uns im selben Verzeichnis wie die Quelldatei(en) befinden:

gnatmake hello

Unter Linux wird dann ein Executable hello erzeugt, unter Windows eine hello.exe. Von der Kommandozeile aufgerufen wird dann der Text „Hello proggen.org!“ ausgegeben. Wenn man es unter Windows per Doppelklick öffnet, schließt sich das Konsolenfenster sofort wieder, da wir vor dem Beenden nicht warten.

gnatmake ist das Ada-Äquivalent von make und führt in diesem Fall folgende Schritte aus:

gcc -c hello.adb
gnatbind -x hello.ali
gnatlink hello.ali

Das erste ist der Compiler. Als Ergebnis werden die Object-Datei hello.o und eine sogenannte „Ada Library Information“-Datei hello.ali erzeugt. Letztere beinhaltet Informationen zu den verwendeten Bibliotheken und deren Versionen, um eine konsistentes Programm zu gewährleisten. Sollte sich zwischen dem gcc-Aufruf und gnatbind oder gnatlink etwas an den Bibliotheken ändern, wird der Compiler mit einer Fehlermeldung abbrechen.

gnatbind führt das Binding des Programms durch. Als Zwischenprodukte werden in unserem Beispiel zwei Dateien erzeugt mit den Namen b~hello.ads und b~hello.adb, die beim anschließenden Linken durch gnatlink kompiliert und wieder automatisch gelöscht werden (außer man benutzt den Debug-Switch -g, dann bleiben die Zwischenprodukte erhalten). Außerdem wird im letzten Schritt das Executable hello bzw. hello.exe erzeugt.