Auf dieser Seite stelle ich Ihnen Regeln vor, die ich beim Schreiben von REXX-Routinen benutze. Alle Regeln, die ich mir beim Schreiben von REXX-Quelltexten angewöhnt habe, basieren auf diesen, die man in C- und Java-Programmen eingesetzt und seit langem benutzt hat.
Die Idee für dieses Dokument ist entstanden, als ich bei einer Projektarbeit bemerkt habe, wie unterschiedlich alle Beteiligten Ihre Quelltexte formatieren. Nach dem Austauschen der Projektmitarbeiter und/oder nach dem Wechsel der Aufgabenbereiche, mussten die Mitarbeiter viel Zeit verlieren, um die von Kollegen programmierten Routinen zu verstehen und an eigene Formatierungregeln umzuschreiben.
Die Einführung und Anhaltung dieser gemeinsamen Regeln hat nach kurzer Einarbeitung enorme Zeitersparnisse gebracht, auch nach der Praxisübergabe für die Wartungsprogrammierer.
In diesem Dokument werde ich folgende meine Regeln detailiert erklären.
Für REXX-Schlüsselwörter, Variablen- und Funktionsnamen verzichte ich (außer in speziellen Fällen) auf das Anwenden von Sonderzeichen.
If name <> '' Then Say name Select When wert > 0 Then ergebnis = ergebnis + value When wert = 0 Then Nop Otherwise Say "Wert muß größer oder gleich Null sein:" wert End
recipeOfTheDay artikelNummer tagesLohn
LoadUtil WriteFromStem ReadToQueue
Ich versuche folgende Regeln beim Verwenden von Hochkommata und Anführungszeichen einzuhalten:
leer = '' crlf = '0A0D'x heute = Date( 'L' ) If Datatype( value ) = 'NUM' ... vorName = Word( "Karolina J. Kowalczyk", 1 )
"Hallo World" waren die ersten Worte des ersten C-Programms.
"""Hallo World"" waren die ersten Worte des ersten C-Programms."
'"Hallo World" waren die ersten Worte des ersten C-Programms.'
'JANOSCH.PR.EXEC' 'SYS1.CMDPROC.EXEC' 'SYS1.CMDPROC.CLIST'
'''JANOSCH.PR.EXEC'' ''SYS1.CMDPROC.EXEC'' ''SYS1.CMDPROC.CLIST'''
"'JANOSCH.PR.EXEC' 'SYS1.CMDPROC.EXEC' 'SYS1.CMDPROC.CLIST'"
schreiben. Entscheiden Sie selbst was informatiever und lesbarer ist.
Der Einrückungsgedanke wurde durch die Darstellungsmöglichkeiten der Bildschirme ausgelöst. Im Textmodus sind es meistens 80 Zeichen x 24 Zeilen. Die möglichen 132 Zeichen im TSO sind für die Programmierung (vor allem für die Programmierer mit Augenproblemen - also für fast alle) meistens unzumutbar und mehr als 43 Zeilen passen auch nicht auf den Bildschirm. In grafischen Editoren auf PC's ist man zwar schon etwas flexibler, aber trotzdem paßt eine REXX-Routine fast nie vollständig ins Fenster. Man ist also zum Scrollen verdonnert! Was allerdings den Überblick über das Programm sehr verschlechtert. Wie kann man sich dagegen wehren? Einfach durch platzsparendes Editieren:
----+----1----+----2----+----3----+----4----+----5----
If value <= preis Then Do
Say "Dein Kontostand rückt in die Gefahrenzone."
Say "Warte lieber bis zum nächsten Zahltag."
End
Else Do
Say "Wieviel Euro möchtest Du ausgeben?"
Pull ausgabe
If Datatype( ausgabe ) = 'NUM' Then
value = value - ausgabe
Else Do
Say "Programm beendet"
Exit
End
End
Sie werden staunen, wieviele von Ihrer REXX-Exec jetzt auf ein Bildschirm mit der Auflösung von 80 Zeichen x 43 Zeilen paßt, ohne dass Sie hier und da scrollen müssen.
Alle meine REXX-Programe haben immer den gleichen modularen Aufbau. Das macht das Lesen des Programms, die Änderungen und die Fehlersuche viel einfacher:
Hier ein Beispiel (für Windows und OS/2), wie der Rahmen von so einem REXX-Programm aussehen könnte:
/* REXX Kommentarblock ****************************************/
/* */
/* Program name: */
/* */
/* Function....: */
/* Syntax......: */
/* Author......: */
/* Create date.: */
/* Version.....: 0.1 */
/* */
/* Changes.....: No */
/* */
/* © Name des Autors, 2008, 2010. All rights reserved. */
/**************************************************************/
Trace o
/*==========( Umgebung- und Parameter-Block )===========*/
Parse Source environment calledAs procName .
If calledAs = "COMMAND" Then
Parse Arg arg1 arg2
Else
Parse Arg arg1, arg2
/*================( Fehlerbehandlung )==================*/
Signal On Failure Name CLEANUP
Signal On Halt Name CLEANUP
Signal On Syntax Name CLEANUP
/*=======( Registrierung von Function-Packages )========*/
Call LoadUtil /* Nur für OS/2 */
/*==================( Programmkern )====================*/
....... zum Beispiel Prozeduraufrufe .................
/*================( Programm-Abschluss )================*/
Call SysDropFuncs
If calledAs = "COMMAND" Then Exit rc
Return rc
/*============( Fehlerbehandlungsprozedur )=============*/
CLEANUP:
Say Space( Errortext( rc ) "RC="rc )
desc = Condition( "D" )
If desc <> '' then desc = "(" || desc || ")"
Say Condition( "C" ) Space( "condition" desc "running" procName )
Say "Sourceline" sigl ">>>" SourceLine( sigl )
Say "Restart please. If the error recurs, notify the author."
Call SysDropFuncs
Exit
Exit rc/*=============( INTERNE REXX-PROZEDUREN )==============*/
/*=====( RexxUtil-Support für OS/2 initializieren )=====*/
LoadUtil: Procedure
If RxFuncQuery('SysLoadFuncs') Then Do
Call RxFuncAdd 'SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs'
Call SysLoadFuncs
End /* If RxFuncQuery... */
Return rc
/*** End LoadUtil ***/
....... Weitere REXX-Prozeduren ........................
Das ist allerdings ein sehr einfaches Beispiel. Etwas mehr umfangreiches können Sie von hier aus laden. Aber trotzdem soll, meines Erachtens nach, ein Programmgerüst so einfach sein, wie es nur geht. Beachten Sie bitte dafür die nächste Regel.
Der große Erfolg von REXX entstand, meiner Meinung nach, dadurch, dass Mike F. Cowlishaw ihn so logisch und einfach gestaltet hat. Diese Sprache hat in der Syntax nichts Überflüssiges. Und obwohl sie so einfach ist, kann sie trotzdem alle Anforderungen erfüllen. Dabei ist das Programmieren mit REXX so einfach als würde man ein ganz normales Gespräch führen. Warum sollen wir das durch das komplizierte Programmieren ändern? Hier einige Beispiele von meisten Problemen und Hinweise, wie man sie vermeiden kann.
Ich versuche für Funktionen oder Variablen keine unverständlichen und langen Namen zu vergeben, womöglich auch ohne Sonderzeichen. Vergleichen Sie bitte einfach diese Variablennamen:
__Recipe_of_the_day__
recipeOfTheDay
oder
_day_#_
dayNumber
Vorteile werden Sie sofort sehen, wenn Sie diese Namen in einer REXX-Exec öfter verwenden.
Zum zerlegen von Zeichenketten wird von den meisten (unerfahrenen) Programmierern die Built-in-Funktion SubsStr verwendet: diese Funktion ist auch aus anderen Programmiersprachen bereits bekannt, die Anwendung ist logisch und einfach. Leider aber auch (woran die meisten nicht denken) sehr unperformant. Hier ein Beispiel (aus echtem Leben!), wie man diese Funktion vergewaltigen kann:
/* Sehr umständlich und deswegen nicht zu empfehlen :( */ heute = Date( "S" ) /* Datum im Format: yyyymmdd */ jahr = SubStr( heute, 1, 4 ) mon = SubStr( heute, 5, 2 ) tag = SobStr( heute, 7, 2 )
Parse Value Date( "S" ) With jahr 5 mon 7 tag .
Dadurch wird nicht nur der Quellcode kürzer und einfacher zum Lesen und Verstehen aber auch das Programm schneller: drei Aufrufe der Funktion SubsStr dauern wesentlich länger als eine Verwendung der PARSE-Anweisung.
Ein weiterer Vorteil besteht durch die Modularisierung des Programms. Es ist eigentlich keine REXX-Erfindung, aber meistens (gerade deshalb, weil das REXX-Programmieren so einfach ist) vergessen das die REXX-Anfänger. Sie programmieren "in einem Atemzug" von Anfang bis zum Ende in einer Prozedur. Da verliert man sich sehr schnell. Und die Pflege und Wartung solcher Monsterprogramme stellt stellt, aus Sicht eines Programmierers, eine Katastrophe dar.
Mein Vorschlag: alles in kleine Prozeduren unterteilen, und das Hauptprogram vorwiegend aus den Aufrufen der Prozeduren aufbauen. Mit möglichst wenig REXX-Codierung. Der zusätzliche Vorteil dieser Methode ist, dass sie viele von diesen Prozeduren wieder verwenden können und viel Zeit sparen.
REXX kennt nur einen Datentyp: Zeichenkette variabler Länge. Diese werden oft in ein weiterer zusammengesetzten Datentyp - Verbund (Satz) zusammen vereinigt. Im Prinzip auch nichts Neues. Es wird in allen höheren Programmiersprachen angewendet, wie z. B. Strukturen oder Unionen in C oder C++. Aber wie kann man das im REXX einstellen? REXX kennt doch keine solchen Datentypen.
Ich habe dafür eine Lösung auf Basis von internen REXX-Prozeduren erarbeitet. Hier beschreibe ich diese Lösung auf Beispiel einer Datenstruktur für einen Datensatz mit dem festen Aufbau - Felder mit den festen Längen und festen Positionen im Satz (der Satz selbst kann allerdings variable Länge besitzen). Diese Methode eignet sich auch für Datensätze mit variablem Aufbau. In diesem Fall entfallen dann die Angaben zur Feldposition und Feldlänge. Man muss allerdings in der Parse-Prozedur Schablonen zum Zerlegen der Sätze definieren.
Die Methode kann man in folgenden Punkten erläutern:Die folgende Beschreibung wird dieses Verfahren an einem Beispiel erläutern. Das Beispiel basiert auf dem Aufbau eines Satzes aus dem im RACF bekanntem Utility IRRDBU00. Der Utility entlädt die RACF-Datenbank in eine Flatfile (eine Datei mit dem festen Satzaufbau pro Satztyp). Wir verarbeiten hier beispielsweise den Satztyp 0260 (s. g. WRKATTR-Segment) Der Satz besteht aus Feldern, die in der folgenden Tabelle zusammengefasst sind.
| Feldname | Feldposition | Feldlänge | Feldbeschreibung |
|---|---|---|---|
| RECORD_TYPE | 1 | 4 | Record type of the User WORKATTR Data record (0260). |
| NAME | 6 | 8 | User ID as taken from the profile name. |
| AREA_NAME | 15 | 60 | Area for delivery. |
| BUILDING | 76 | 60 | Building for delivery. |
| DEPARTMENT | 137 | 60 | Department for delivery. |
| ROOM | 198 | 60 | Room for delivery. |
| ADDR_LINE_1 | 259 | 60 | Address line 1. |
| ADDR_LINE_2 | 320 | 60 | Address line 2. |
| ADDR_LINE_3 | 381 | 60 | Address line 3. |
| ADDR_LINE_4 | 442 | 60 | Address line 4. |
| ACCOUNT | 503 | 255 | Account number. |
Basierend auf der oberen Tabelle wurden folgende Daten-Prozeduren definiert:
1. Prozedur zum Zerlegen des Satzes in einzelne Felder. Bitte beachten, dass die Trennzeichen (in dem Fall Leerzeichen) in ein "." (Punkt) - den Platzhalter der PARSE-Anweisung - eingelesen sind.
/*==================( Parse input record of USWRK )==================*/
UswrkParse: Procedure Expose uswrk.
Parse Arg 1 uswrk._recordType , /* Record type of the User WORKATTR */
5 . , /* Data record (0260). */
6 uswrk._name , /* User ID as taken from the */
14 . , /* profile name. */
15 uswrk._areaName , /* Area for delivery. */
75 . ,
76 uswrk._building , /* Building for delivery. */
136 . ,
137 uswrk._department , /* Department for delivery. */
197 . ,
198 uswrk._room , /* Room for delivery. */
258 . ,
259 uswrk._addrLine1 , /* Address line 1. */
319 . ,
320 uswrk._addrLine2 , /* Address line 2. */
380 . ,
381 uswrk._addrLine3 , /* Address line 3. */
441 . ,
442 uswrk._addrLine4 , /* Address line 4. */
502 . ,
503 uswrk._account , /* Account number. */
758 .
Return uswrk._name
/*** End UswrkParse ***/
2. Prozedur zur Beschreibung des Satzes und Felder.
/*==================( Description values of USWRK )==================*/ UswrkDesc: Procedure Expose uswrk. /*==========( Length block of UswrkDesc )===========*/ uswrk._len. = 0 uswrk._len._recordType = 4 uswrk._len._name = 8 uswrk._len._areaName = 60 uswrk._len._building = 60 uswrk._len._department = 60 uswrk._len._room = 60 uswrk._len._addrLine1 = 60 uswrk._len._addrLine2 = 60 uswrk._len._addrLine3 = 60 uswrk._len._addrLine4 = 60 uswrk._len._account = 255 /*=========( Position block of UswrkDesc )==========*/ uswrk._pos. = 0 uswrk._pos._recordType = 1 uswrk._pos._name = uswrk._pos._recordType + uswrk._len._recordTyp + 1 uswrk._pos._areaName = uswrk._pos._name + uswrk._len._name + 1 uswrk._pos._building = uswrk._pos._areaName + uswrk._len._areaName + 1 uswrk._pos._department = uswrk._pos._building + uswrk._len._building + 1 uswrk._pos._room = uswrk._pos._department + uswrk._len._departmen + 1 uswrk._pos._addrLine1 = uswrk._pos._room + uswrk._len._room + 1 uswrk._pos._addrLine2 = uswrk._pos._addrLine1 + uswrk._len._addrLine1 + 1 uswrk._pos._addrLine3 = uswrk._pos._addrLine2 + uswrk._len._addrLine2 + 1 uswrk._pos._addrLine4 = uswrk._pos._addrLine3 + uswrk._len._addrLine3 + 1 uswrk._pos._account = uswrk._pos._addrLine4 + uswrk._len._addrLine4 + 1 Return uswrk._pos._account + uswrk._len._account - 1 /*** End UswrkDesc ***/
3. Prozedur zum Erstellen eines Satzes in dem vorgegebenen Format.
/*======================( Create record USWRK )======================*/ UswrkCreate: Procedure Expose uswrk. Parse Arg rec rec = Overlay( uswrk._recordType, rec, uswrk._pos._recordType, uswrk._len._recordType ) rec = Overlay( uswrk._name, rec, uswrk._pos._name, uswrk._len._name ) rec = Overlay( uswrk._areaName, rec, uswrk._pos._areaName, uswrk._len._areaName ) rec = Overlay( uswrk._building, rec, uswrk._pos._building, uswrk._len._building ) rec = Overlay( uswrk._department, rec, uswrk._pos._department, uswrk._len._department ) rec = Overlay( uswrk._room, rec, uswrk._pos._room, uswrk._len._room ) rec = Overlay( uswrk._addrLine1, rec, uswrk._pos._addrLine1, uswrk._len._addrLine1 ) rec = Overlay( uswrk._addrLine2, rec, uswrk._pos._addrLine2, uswrk._len._addrLine2 ) rec = Overlay( uswrk._addrLine3, rec, uswrk._pos._addrLine3, uswrk._len._addrLine3 ) rec = Overlay( uswrk._addrLine4, rec, uswrk._pos._addrLine4, uswrk._len._addrLine4 ) rec = Overlay( uswrk._account, rec, uswrk._pos._account, uswrk._len._account ) Return rec /*** End UswrkCreate ***/
Diese Prozeduren müssen in das Block INTERNE REXX-PROZEDUREN eines REXX-Programmes kopiert werden und danach können sie folgendermaßen verwendet werden (hier ein sehr vereinfachtes Beispiel, das nur diese Idee vermitteln und erläutern soll:
dsnINP = "'RACF.FLATFILE.USERS(USWRK)'"
ddnINP = "USWRKINP"
rc = AllocIt( dsnINP, ddnINP )
rc = Read( ddnINP )
...
Call UswrkDesc
...
Do i = 1 To stem.0
userid = UswrkParse( stem.i )
Say Left( userid, uswrk._len._name ) uswrk._room
If userid = "USR0001" Then Do
Say "Frau" usrwrk._name "hat geheiratet."
uswrk._name = "MUSTERFRAU,MARIA"
Say "Ihr neuer Name lautet:" usrwrk._name
rec = UswrkCreate( rec )
Queue rec
End
...
End
...