[ Inhalt ] [ Index ] Domaincompiler Analyse der VDM Klassenbibliotek Klassenbibliothek

Preprozessor

Der Preprozessor der VDM Klassenbibliothek stammt aus dem Jahre 1991. Der Programmname des Preprozessors lautet vdmpp. Als Ersteller ist ein Herr Schlotfeldt im Quellcode vermerkt. Der Preprozessor ist vollständig in der Hochsprache C erstellt, auf den Einsatz von Compilerbautools wie Lex oder Yacc wurde verzichtet. Es handelt sich bei vdmpp um einen vollständigen ANSI-C Preprozessor, der jedoch eine Übermenge ( superset) der für ANSI-C definierten Strukturen verarbeiten kann.  

Der vdmpp ist ebenso wie ein ANSI-C Preprozessor ein Makro-Prozessor, der vor der entgültigen Übersetzung des Quelltextes durch den Compiler eine Vorverarbeitung vornimt. Er wird als Makro-Prozessor bezeichnet, da er in der Lage ist, Makros, die eine Abkürzung für längere Konstrukte sind, zu substituieren. Das Zeichen ,,#`` spielt die entscheidene Rolle bei der Identifikation der Makros.

Die wesentlichen Funtionen des Preprozessors sind:

Die Elemente, die durch einen ANSI-C Preprozessor verarbeitet werden, sollen in Kürze dargestellt werden, um die Funktionsweise zu analysieren. Im Anschluß erfolgt die Dartstellung der erweiterten Möglichkeiten durch den vdmpp.

  1. Globale Transformationen

    Ersetzung von Kommentaren sowie Rückstrich-Seitenumbrüchen durch Leerzeichen als auch von vordefinierten Makros durch deren Erweiterung

  2. Preprozessorkommandos

    Interpretation der Preprozessor-Kommandos, die mit ,,#`` beginnen. Der Kommandosatz des Preprozessors ist fest und kann nicht zur Laufzeit erweitert werden. Bekannte Kommandos in diesem Zusammenhang sind ,,#ifdef``, ,,#if``, ,,#else``, ,,#elseif``, ,,#endif`` als auch ,,#undef``.

  3. Dateieinbindung

    Mit Hilfe des #include Preprozessorkommandos können weitere Dateien in den zu verarbeitenden Quelltext eingebunden werden.

  4. Einfache Makros

    Einfache Beispiele hierfür sind

            #define BUFFER 1024
            #define BUFLEN BUFFER

  5. Makros mit Argumenten

    Definition von Makros als Funktiondeklaration(*), wie folgende Beispiele zeigen

            #define min(x,y) ((x) < (y) ? (x):(y))
            #define foo(x) -1 / (x)
            #define foo2 (x) -1 / (x)

    Beachtenswert ist, daß das Makro foo der obigen Beispiele durch -1 / (x) ersetzt wird, wogegen aufgrund des Leerzeichens bei foo2 die Substitution durch (x) -1 /x (x) erfolgt.

  6. Stringumwandlung (stringification)

    Definition von Makros in Form einer Funktionsdeklaration, wie folgende Beispiele demonstrieren

            #define str(x) #x
            #define NOTE(notification) \
                    fprintf(stderr,"Please note: #notification ");

    Die Ersetzung mittels stringification führt mehr aus, als die bloße Umrandung des Textes mit Anführungszeichen. Damit ein gültiger String ensteht, werden die in Zeichenketten verwendeten Escape-Codes durch den Preprozessor eingesetzt.

  7. Verbindung (concatenation) Verbindung von Elementen im Makrorumpf durch den operator ,,##``

            #define NEWENTRY(name) { #name, name ## _entry }

    Der folgende Aufruf dieses Makros

            struct {char *name, void(*function) () } list [] = 
            {
            NEWENTRY(foo) ,
            NEWENTRY(test)
            }

    ergibt substituiert

            struct {char *name, void(*function) () } list [] = 
            {"foo", foo_entry },
            {"test", test_entry }
            };

    Zu beachten ist bei der Verbindung durch den ,,##``-Operator, daß Leerzeichen und Tabulatoren (whitespaces) vor und nach dem Operator entfernt werden. Der Verbindung von Ausdrücken kommt bei der Anwendung der VDM Klassenbibliothek eine außerordentlich wichtige Rolle zu. Die Typengenerierung eines ADTs auf eine Domäne für C erfolgt bei der Umsetzung durch eine Zusammensetzung der Operationsbezeichner aus dem Namen der Operation sowie des Domänennamens. Eine Umsetzung in C++ basiert auf den für C generierten Funktionen, die von den Methoden der Klassen verwendet werden, dessen Klassenname dem Domnänennamen entspricht.

Der VDM Preprozessor von Schlotfeldt kennt zusätzlich zum ANSI-C Prozessor weitere Funktionen, die für die Umsetzung der derzeitigen Inhalte der VDM Klassenbibliothek von außerordentlicher Bedeutung sind. Schlotfeldt verweist im Quellcode hinsichtlich der Details auf die externe Dokumentation, die dem Autor dieser Diplomarbeit jedoch leider nicht vorliegt. Die Besonderheiten des VDM Preprozessors wurden daher anhand der Bibliotheksinhalte sowie des Quellcodes ermittelt, weshalb dem Anspruch nach Vollständigkeit an dieser Stelle nicht Rechnung getragen werden kann.  

Die Ergänzungen des VDM Precompilers zum ANSI-C Preprozessors sind im wesentlichen:

  1. Globale Stringumwandlung

    Textzeilen können an jeder Stelle, ohne daß es der Verwendung eines Makros bedarf, durch den ,,''''#``-Operator (Zwei Doppelhochkommata gefolgt vom Doppelkreuz) am Zeilenbeginn, in eine Zeichenkette gewandelt werden. Wird das Zeichen als solches benötigt, wird diesem als Escape-Metazeichen ein Rückstrich (backslash) vorangestellt.

  2. Globale Stringverbindung

    Bei diesem Feature handelt es sich um die wohl wichtigste Erweiterung zum ANSI Standard. Fast sämtliche Codefragmente der VDM Datenbank sind auf diese Möglichkeit angewiesen. Der Einsatz soll beispielhaft demonstriert werden:

            ELEM2 checkread##_DOMAIN _FPH1(R2##_DOMAIN)
                    { x1 = ovwrt_rd(x1,fread##_ELEM2(x2)); }

    Der Leser wird gebeten, nicht zu versuchen, die Logik dieser Codierung zu verstehen, denn sie hat keine. Aufmerksamkeit ist jedoch den lexikalischen Verbindungen checkread##_DOMAIN , R2##_DOMAIN und (fread##_ELEM2(x2)) zu schenken. Es wird an diesen Stellen eine Stringverbindung hergestellt, ohne daß es sich um eine Makrodefinition oder einen Makrorumpf handelt. Die Verbindung erfolgt inmitten des Textes. Das obige Beispiel demonstriert, wie anhand von Domänen gemeinsam mit Operationen Methoden und Funktionen für den Quelltext der Hochsprache erzeugt wird.

  3. Entfernen doppelter Leerzeilen und sonstige Features

    Der Preprozessor ist ferner zu einer Entfernung von doppelten Leerzeichen, automatischer Stringverbindung und sonstigen Substitutionen in der Lage, dessen Nützlichkeit für die VDM Datenbank jedoch nicht mit der globalen Stringverbindung gleichzieht. Zu einer genaueren Beschreibung wird hier auf die Dokumentation des Erstellers Schlotfeldt verwiesen.

Kritik am Preprozessor

Abschließend soll (konstruktive) Kritik am Preprozessor nicht fehlen. Die Kritikpunkte betreffen genauso Feststellungen, die der Autor während der Bearbeitung der Diplomarbeit, treffen mußte als auch (für den Autor) wünschenswerte Erweiterungen.

Auffällig ist, daß der vdmpp alle Funktionen des ANSI-C Preprozessor unterstützt, jedoch nicht als vorzuziehender Preprozessor für den ANSI-C Prozessor in Durchführung zweier Übersetzungsläufe konzipiert ist, was den Umfang des Programmes erheblich reduziert hätte(*).

Die Methode der Zusammensetzung von Konstrukten ist zwar geeignet, die Umsetzung der DSL in Hochsprachen zu gestalten, sie schafft jedoch nur einen geringen Abstraktionsgrad, der das Verstehen und den Überblick über die Codefragmente erschwert und damit eine Fehleranfälligkeit mit sich bringt.

Der Autor hätte anstelle der Implementierung des ANSI-C Standards für den Preprozessor eine mehr problemorientierte an BNF angelehnte Formulierungsmöglichkeit bevorzugt, auch um Makros des VDM Preprozessors von den Makros der Zielsprachen C und C++ abzusetzen - selbst wenn es sich bei den Codefragmenten um Interna der Klassenbiliothek handelt. Transparenzprobleme wurden insbesondere darin gesehen, daß Makros, die keine Argumente erwarten, nicht von Text zu unterscheiden sind, wodurch zum einen unklar ist, ob es sich um ein Makro handelt und zum anderen die Gefahr besteht, daß versehentlich Text durch Makros ersetzt werden könnte.

Ein weiterer Kritikpunkt betrifft die Sonderzeichenverarbeitung des Preprozessors. Makrodefinitionen sind lediglich alphanumerisch vornehmbar, was den Unterstrich einschließt. Unverwechselbare, kryptische Makros sind hiermit nicht formulierbar. Ferner ist durch den Preprozessor die Verarbeitung von Makros mit einer variablen Anzahl von Argumenten nicht möglich, was zu beispielsweise zu folgenden Konstrukten in der Datenbank führt:

        #define FPAR1(t1) ()
        #define FPAR2(t1,x1) (x2)
        #define FPAR3(t1, x2  ,x3) (x2, x3)
        usw.

Als Konsument des VDM Preprozessors, sind für den Autor Ansprüche dieser Art leicht formulierbar, er übt diese Kritik an dieser Stelle jedoch aus der Sicht als ,,Anwender`` unter Verdrängung des Realisierungsaufwandes.

Bei der Diagnose des Datenbankmanagers wurde festgestellt, daß das Einbinden von Dateien, beispielsweise durch #include <stdlib/aux/setup/com> für das ursprüngliche DIRECTORY-Modell ausgelegt ist, also für die Einbindung von physikalischen Dateien. Im FLAT-Modell führt die Verarbeitung des #include Kommandos zwangsweise zu Fehlern, da lediglich eine Bibilotheksdatei existiert. Als Umgehung (workaround) werden von allen Tools der Klassenbibliothek im FLAT-Modell alle Einbettungsanweisungen vor dem Aufruf des Precompilers ausgeführt.

Der Preprozessor ist ebenfalls im Domaincompiler, der im folgenden Abschnitt beschrieben wird, als Bibliothek eingebunden. Auch hier gilt, daß Einbettungsanweisungen vor der Vorverarbeitung aufgelöst werden müssen. In diesem Zusammenhang wird die Frage aufgeworfen, ob der Preprozessor als separates Programm überhaupt noch seine Existenzberechtigung hat, zumal der Quellcode des Preprozessors in der Entwicklungsinstallation der Klassenbibilothek auch doppelt vorgehalten wird(*).


[ Inhalt ] [ Index ] Domaincompiler Analyse der VDM Klassenbibliotek Klassenbibliothek

VDM Class Library