Software Wartung und Evolution | Portierung eines Altsystems von PL/I nach Java

Im Rahmen der Lehrveranstaltung Software Wartung und Evolution musste ich ein PL/I Programm analysieren, dokumentieren und nach Java portieren. Im restlichen Post beschreibe ich Schritt für Schritt, wie ich bei dieser Übung vorgegangen bin. Ich bin in meiner bisherigen IT-Laufbahn noch nie mit PL/I in Berührung gekommen und daher ein absoluter Beginner bezüglich dieser Programmiersprache. Nachdem ich das zu untersuchende Programm von der Lehrveranstaltungshomepage heruntergeladen und kurz überflogen hatte, war meine erste Idee im Web nach einem PL/I Compiler zu suchen um ein paar Codebeispiele selbst zu tippen, zu kompilieren und auszuführen.

Nach ein bisschen googlen musste ich leider feststellen, dass diese Programmiersprache nicht mehr wirklich supported bzw. weiterentwickelt wird. Allerdings hatte ich Glück eine Demo-Version einer PL/I-Programmierumgebung von IBM in einem Forum zu finden.  Ich habe die Datei pliwintb.zip heruntergeladen und in einer VMWare unter Windows XP installiert. Nach kurzer Einarbeitungsphase, habe ich das erste “Hello World”-Programm erfolgreich kompiliert, ge-linkt und ausgeführt.

PL/I Hello World Demo

 

 

 

 

 

 

Mit Hilfe des Buchs “Das neue PL/I” habe ich mich ein bis zwei Stunden in PL/I eingelesen und ein paar Beispielprogramme abgetippt und ausgeführt. Danach habe ich begonnen, das für die Uni-Übung zu analysierende Programm Zeile für Zeile selbst zu implementieren und zu testen. Falls mir Schlüsselwörter und Sprachkonstrukte noch nicht bekannt waren, habe ich diese einfach in dem weiter oben erwähnten Buch nachgeschlagen. Falls mir dann immer einige Dinge noch unklar waren, habe ich einfach ein kurzes Programm geschrieben, um die mir noch unverständliche Funktionalität zu testen.

Die folgende Aufgabenstellung und der zugehörige Tipp ist von der Lehrveranstaltungs-hompage entnommen. Danach gebe ich eine Ausführliche Dokumentation des zu portierenden PL/I-Programmes.

Aufgabenstellung

Im Rahmen der Analyse und Portierung eines Altsystems von PL/I nach Java, konnte der Großteil der Arbeit bereits erfolgreich durchgeführt werden. Ein kleiner Programmteil konnte jedoch – mangels Dokumentation – noch nicht analysiert und portiert werden.

Ihre Aufgabe ist es, den verbliebenen Teil des PL/1 Sourcecodes zu analysieren, dessen Funktionsweise zu erklären und den Sourcecode geeignet zu re-dokumentieren. Weiters soll die Funktionsweise des PL/1 Programms in Java übersetzt/portiert werden.

Hier ein von mir erstelltes Gist auf Github des Progamms: PROGRAMXYZ.pli

Tipps

Ev. gibt es einen guten Grund warum die ursprünglichen Programmierer genau diesen Programmteil nicht dokumentiert hatten.

Es ist nicht notwendig (und unter Umständen auch nicht ohne weitere Änderungen möglich) den bestehenden Code lauffähig zu bekommen. Sollten Sie dennoch versuchen, den Code direkt zu übersetzen und Erfolg dabei haben, können Sie Zusatzpunkte erlangen.

Codeanalyse

Nachdem ich das Programm genau analysiert und einige mögliche Benutzereingaben auf altmodische Weise, nämlich mit Zettel und Bleistift, durchgespielt hatte, wurde die Funktionalität des Programms eindeutig. Es handelt sich um das bekannte Spiel “Master-Mind”. 

In den folgenden Absätzen beschreibe ich die Teile des Quellcodes, von denen ich nicht wusste, was sie genau tun und wofür sie da sind. Um meiner Beschreibung besser folgen zu können, würde ich empfehlen, den kompletten Quellcode des Programms herunterzuladen und jeweils die von mir beschriebene Zeile nachzuschlagen.  

Aufgrund der ersten Zeile des Programms weiß der Compiler (zumindest der von IBM) alles über deutsche Buchstaben: 

*process names ('äöüß', 'ÄÖÜ$');

Der folgende Programmausschnitt definiert die Methodensignatur des Hauptprogamms. Man kann diese Methode mit der Main-Methode eines jeden lauffähigen Java-Programms vergleichen Bei dem Schlüsselwort reorder handelt es sich um eine Optimierungsangabe für den Compiler. Es bewirkt, dass der Compiler gewisse Anweisungen beliebig umordnen kann, wenn dadurch die Ausführungszeit verkürzt wird.

 PROGRAMXYZ:
 procedure options (main reorder);

Für alle möglichen Farbeingaben wird ein eigener Datentyp definiert. Hier wäre die Angabe von tatsächlichen Farben statt Optionen für die Benutzung des Programms “schöner” gewesen. Anstatt vom Benutzer Optionen zu verlangen, wäre die Angabe von z.B. “grün blau rot grün” noch spielgetreuer gewesen:

 define ordinal COLOR 
    (INVALID, OPTION1, OPTION2, OPTION3, OPTION4, OPTION5, OPTION6);

Für die Tipps des Benutzers und für die „ausgedachte“ Wahl (Code) des Computers werden als Datentypen Matrizen verwendet.

 dcl Code dim (4) type COLOR;
 dcl TIP dim (4) type COLOR;

Nach der Deklaration aller notwendigen Variablen wird durch den Aufruf der Methode RANDOM die “Code”-Matrix befüllt. Dabei handelt es sich also um die vom Spieler zu erratene Farbwahl des Computers. Per Definition wird die RANDOM-Methode vier Mal aufgerufen, da es sich um eine Matrix mit der entsprechenden Dimension handelt. Dabei handelt es sich mein Meinung nach um eine äußerst interessante Funktionalität, dich ich so in noch keiner anderen Programmiersprache gesehen habe.

 Code = RANDOM();

RANDOM-Funktion unter die Lupe genommen:

Diese Methode gibt eine gültige Farboption, die zufällig gewählt wird, zurück. Dieser Zufallsmechanismus funktioniert wie folgt: die von PL/I zur Verfügung gestellte random-Funktion ergibt eine Zahl zwischen 0 und 1, wobei 0 und 1 ausgeschlossen sind. Wenn man diese Zahl nun mit sechs multipliziert, ergibt das natürlich eine Zahl zwischen 0 und 6. Um diese als Index für die COLORCODE Matrix verwenden zu können, benötigt man die PL/I-Funktion trunc, die einfach den Nachkommateil der Zahl verwirft.

 RANDOM:
 procedure returns (type COLOR);
   dcl Zahl float init (0) static;
   dcl COLORCODE dim (0:5) type COLOR nonasgn static
      init (OPTION1, OPTION2, OPTION3, OPTION4, OPTION5, OPTION6);

   if Zahl = 0
      then Zahl = random(time());
      else Zahl = random();
   return (COLORCODE(trunc(Zahl*6)));
 end RANDOM;  

Nach dem initialsieren der Code-Matrix, d.h. der Farbwahl des Computers,  ist der Benutzer am Zug. Seine Eingabe wird innerhalb einer Endlosschleife im Hauptprogramm entgegengenommen.

 do loop;
    display ('TIP?') reply (ANSWER);
    ...
    end;

Danach wird vom Programm versucht, die Benutzereingabe in einen gültigen Tipp umzuwandeln. Dabei wird versucht, die Eingabe zu parsen und in eine Matrix zu konvertieren. Diese Aufgabe übernimmt wiederum eine kurze Subroutine namens TRANSLATION die vom Hauptprogramm aus aufgerufen wird.

call TRANSLATION (ANSWER, TIP);

TRANSLATION-Funktion unter die Lupe genommen:

Der erste Schritt ist das Initialisieren der TIP-Matrix mit dem Wert INVALID. Danach wird die Benutzereingabe, mittels PL/I zur Verfügung gestellter translate-Funktion, in Großbuchstaben umgewandelt.  Interessant sind auch die beiden PL/I-Funktionen verify und search. In Ersterer wird das erste Argument darauf hin untersucht, ob es ausschließlich aus den Zeichen des zweiten Arguments besteht. Das dritte Argument gibt die Position an, ab der diese Bedingung Gültigkeit erlangen soll. Verify gibt entweder 0 zurück, sofern alle Zeichen gültig sind, oder die Position des ersten nicht gültigen Zeichens. Die sehr ähnliche search-Funktion durchsucht das erste Argument nach dem ersten Vorkommnis des als zweiten Argument übergebenen Zeichens.

Trans

Sobald die notwendigen Positionen (für Letter und Blank) ermittelt worden sind, wird mittels substring-Methode, das sich durch die Positionen ergebende Wort, aus der Benutzereingabe herauskopiert. Dieses wird danach auf Gültigkeit hin überprüft, d.h. handelt es sich um eine valide Farboption, und gegebenenfalls an der entsprechenden Position in der Tipp-Matrix gespeichert. 

 TRANSLATION:
 procedure (ANSWER, TIP);
/* Variablen-Deklaration */



TIP = INVALID; if length(ANSWER) = 0 then return; S = translate(ANSWER,'ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜ', 'abcdefghijklmnopqrstuvwxyzäöü'); Blankpos = 1; do I = 1 to 4; Letterpos = verify(S, ' ', Blankpos); if Letterpos = 0 then leave; Blankpos = search(S, ' ', Letterpos); if Blankpos = 0 then Blankpos = length(S)+1; do F = OPTION1 upthru OPTION6; if substr(S, Letterpos, Blankpos-Letterpos) = ordinalname(F) then TIP(I) = F; end; end; end TRANSLATION;

Nach dem Aufruf der TRANSLATION-Methode hat man eine initialisierte Tipp-Matrix die nun auf Richtigkeit hin überprüft werden kann. Die any-Funktion wird von PL/I zur Verfügung gestellt und erwartet als Argument eine Matrix. Es wird für jedes Element in TIP überprüft ob es den Wert INVALID besitzt. Als Ergebnis erhält man eine neue Matrix die nur aus Bit-Werten besteht. Sofern eines der Felder in der neuen Matrix auf den Wert Wahr (1’b’) gesetzt wurde und somit ursprünglich den Wert INVALID hatte, ergibt die any-Funktion ebenfalls wahr (OR-Verknüpfung).

 if any(TIP = INVALID) then

display ('INVALID COLOR!');

Die all-Funktion verhält sich ähnlich zur any-Funktion jedoch handelt es sich hier um eine AND-Verknüpfung, d.h. alle Elemente der übergebenen Matrix müssen den Wert Wahr (‘1’b) besitzen. In diesem Fall muss jede Farboption des Tipps der jeweiligen Farbeoption der Code-Matrix entsprechen. Ist das der Fall, handelt es sich um einen richtigen Benutzertipp und die do-Schleife wird unterbrochen und das Programm beendet.

 if all(TIP = Code) 
    then leave;

Ansonsten wird mittels einer weiteren Subroutine namens EVALUATION diejenigen Treffer des Tipps ermittelt, die entweder die richtige Farbe und richtige Position (HITS) oder nur die richtige Farbe (HITS_OTHER_POSITION) besitzen. 

 do loop;
    ...
    call EVALUATION (Code, TIP, HITS, HITS_OTHER_POSITION);
    ...

EVALUATION-Funktion unter die Lupe genommen:

 EVALUATION: 
 procedure (Code, Tip, HITS, HITS_OTHER_POSITION);
   
/* Variablen-Deklaration */
HITS = sum(Tip = Code); HITS_OVERALL = 0; do F = OPTION1 upthru OPTION6; HITS_OVERALL += min(sum(Tip = F), sum(Code = F)); end; HITS_OTHER_POSITION = HITS_OVERALL - HITS; end EVALUATION;

Wie auch schon bei der any- und all-Funktion, handelt es sich bei dem übergebenen Argument der von PL/I zur Verfügung gestellten sum-Funktion, um eine Bit-Matrix. Wenn also die in der Tip-Matrix eingetragenen Farboptionen mit denen der in der Code-Matrix übereinstimmen, werden alle Elemente die den Wert Wahr (1’b’) besitzen, aufsummiert (Hits). Um die Anzahl der Treffer zu berechnen, bei denen nur die Farbe korrekt ist, wird zuerst in einer do-Schleife für jede mögliche Farboption, die Anzahl der gesamten Treffer ermittelt und aufsummiert. Danach werden von den gesamten Treffern die Hits abgezogen und man erhält genau die Treffer bei denen nur die Farbe korrekt ist.

EVAL

Programmterminierung

Die Endlosschleife des Hauptprogamms wird erst dann unterbrochen, bis die Tipp- und die Code-Matrix übereinstimmen oder der Benutzer eine leere Eingabe  macht. D.h. der Benutzer spielt so lange, bis er die richtige Kombination erraten hat oder bis er das Spiel durch eine leere Zeichenkette abbricht.

Zusatzpunkte

In der Aufgabenstellung findet man unter Tipps den Hinweis, dass das zu untersuchende Programm nicht ohne Änderungen lauffähig sei. Zusatzpunkte könne man dadurch erreichen, indem man die Ursache für für das Nichtfunktionieren herausfindet und dann das Programm so abändert, damit es lauffähig wird.

Was mir beim ersten Überfliegen des Programms sofort aufgefallen ist, ist die Namensgleichheit der selbst erstellten Funktion RANDOM und der entsprechenden PL/I-Funktion. Da diese Funktion leider nicht mit meinem gefunden Compiler funktioniert, konnte ich das Programm auch nicht lauffähig bekommen. Allerdings habe ich ein kurzes Programm geschrieben, dass zumindest zeigt, dass eine solche Namensgleichheit nicht vorkommen darf.

Programm für das Aufzeigen eines Namenkonflikts

 

 

 

 

 

 

 

Fazit

Obwohl es sich bei PL/I um eine doch etwas in die Jahre gekommene Programmier-sprache handelt, finde ich doch einige Sprachkonstrukte sehr interessant. Zum Beispiel die Matrizen-Operationen wie any oder all finde ich äußerst gut lesbar und leicht zu verstehen.

Über sageniuz

https://about.me/ClausPolanka
Dieser Beitrag wurde unter University abgelegt und mit verschlagwortet. Setze ein Lesezeichen auf den Permalink.

Eine Antwort zu Software Wartung und Evolution | Portierung eines Altsystems von PL/I nach Java

  1. sebastiankuebeck schreibt:

    PL/1 hat tatsächlich einige Schmankerln zu bieten, allerdings ist diese Sprache letztlich unter ihrem eigenen Gewicht zusammengebrochen. Edsger Dijkstra schrieb 1972*:

    Using PL/1 must be like flying a plane with 7000 buttons, switches and handles to manipulate in the cockpit. I absolutely fail to see how we can keep our growing programs firmly within our intellectual grip when by its sheer baroqueness the programming language —our basic tool, mind you!— already escapes our intellectual control. And if I have to describe the influence PL/1 can have on its users, the closest metaphor that comes to my mind is that of a drug. I remember from a symposium on higher level programming language a lecture given in defense of PL/1 by a man who described himself as one of its devoted users. But within a one-hour lecture in praise of PL/1. he managed to ask for the addition of about fifty new „features“, little supposing that the main source of his problems could very well be that it contained already far too many „features“. The speaker displayed all the depressing symptoms of addiction, reduced as he was to the state of mental stagnation in which he could only ask for more, more, more… When FORTRAN has been called an infantile disorder, full PL/1, with its growth characteristics of a dangerous tumor, could turn out to be a fatal disease.

    * http://www.cs.utexas.edu/~EWD/transcriptions/EWD03xx/EWD340.html

Schreibe einen Kommentar

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s