Bibliotheken für Basic: Libraries in Omikron-Basic entwickeln, Teil 1

Im ersten Teil zeigten wir die Vorteile einer Basic-Library im vergleich zum Quelltext und erklärten deren Struktur. Diesmal erfahren Sie, welchen Anforderungen der Quelltext genügen muß, und erhalten Informationen zum Hauptprogramm - dem Library-Maker.

Die »Linker ähnlichen« Fähigkeiten des Compilers erfordern einen modularen Aufbau des Basic-Programms. Nur so ist gewährleistet, daß unbenutzte Teile nicht in das Programm eingebunden werden. Anstelle der unter C üblichen Klammern treten hier DEF und RETURN. Das heißt, eine Library besteht ausschließlich aus Funktionen und Prozeduren, die jeweils mit RETURN abgeschlossen sind (Ausnahme: einzeilige Funktionen). Ein Hauptprogramm gibt es nicht. Folgende Konstruktionen sind problematisch bzw. verboten:

Verwenden Sie niemals GOTO, außer innerhalb einer in sich geschlossenen Prozedur oder Funktion.Verläßt GOTO eine Prozedur oder Funktion, erkennt dies der Compiler nicht und das Sprungziel bleibt unter Umständen undefiniert. Dieser Fall tritt dann ein, wenn Sie die Zielprozedur nicht verwenden und der Compiler sie nicht einbindet. Der Compiler hängt sich dann während der Compilierung auf. Dieses Problem überprüft der Library-Maker nicht. Es liegt ganz in der Verantwortung des Programmierers. Deshalb am besten kein GOTO verwenden - es läßt sich sowieso immer vermeiden.

Wenn Sie aus der aktuellen Prozedur oder Funktion herausspringen müssen, weil die Library z. B. eine Fehlerbehandlung mit ON ERROR GOTO verwirklicht, so muß sichergestellt sein, daß das Sprungziel im Compilat eingebunden wird. Hierzu zwei Verfahren:

  1. Sie definieren die Fehlerbehandlung in einer Art Init-Prozedur, die das Programm vereinbarungsgemäß vor dem Benutzen der Library aufrufen muß.

  2. Sie stellen mit einem Pseudo-Aufruf die Einbindung sicher:

    DEF PROC Open_Adress_Datei ON ERROR GOTO Globale_Fehlerbehandlung_ OPEN „U“, ... ... RETURN

Wollen Sie eine solche Fehlerbehandlungs-Routine von mehreren Prozeduren aus nutzen, ist es nicht sinnvoll, sie in eine bestimmte Prozedur mit einzubinden. Sie sollte in der INIT-Prozedur oder in einer eigenen speziellen Prozedur vereinbart werden.

DEF PROC My_Library_Init	´ Init-Prozedur
	...
	GOTO Init_End
	-Globale_Fehlerbehandlung_
		...
	RESUME ...
	-Init_End
RETURN

Da die INIT-Prozedur immer aufgerufen werden muß, ist auch die Fehlerbehandlung immer eingebunden. Der zweite Weg ist eine eigene Prozedur, welche die Fehlerbehandlung enthält:

DEF PROC Fehler_
	-Globale_Fehlerbehandlung_
		...
	RESUME .....
RETURN

Diese Prozedur rufen Sie über den »IF 0 THEN Fehler_« auf. Ein solcher Pseudo-Aufruf muß überall dort zu finden sein, wo Sie die Fehlerroutine verwenden. Fehler im Zusammenhang mit nicht eingebundenen Library-Teilen erkennt der Compiler nicht. Sie führen zwangsläufig zu nicht compilierbaren Libraries. Alles was über GOTO im obigen Abschnitt gesagt wurde, gilt sinngemäß auch für DATAs. In diesem Fall achten Sie darauf, die DATA-Anweisungen einzubinden. Dies erreichen Sie am besten, indem Sie die DATAs direkt in die zugehörige Prozedur packen. Ist dies nicht möglich oder unpraktisch, da mehrere Prozeduren auf die DATAs zugreifen, so beschreiten Sie einen der bereits genannten Wege. Die Verwendung mehrerer Ausgänge bei mehrzeiligen Funktionen birgt eine weitere Schwierigkeit. Abgesehen von der manchmal etwas größeren Unübersichtlichkeit gelingt es dem Compiler bei mehreren RETURNs nicht, das Ende einer Funktion zu erkennen. Wie oben schon erwähnt sind DEF und RETURN die Begrenzungsmarken. Bei mehreren RETURNs nach einem DEF ist es deshalb nicht mehr möglich zu entscheiden, an welchem Punkt die Funktion zu Ende ist. Es ist unumgänglich, eine zusätzliche Rückgabevariable zu vereinbaren. Man weist ihr den Rückgabewert zu und gibt sie dann am einzigen Funktionsausgang mit »RETURN Variable« zurück. Auch hier ein kurzes Beispiel:

DEF FN Fakultaet# (X#)
	IF X# = l# THEN
		RETURN l#
	ELSE
		RETURN Funktion Fakultaet# (X# - l#)
	ENDIF
RETURN				´ ist eigentlich unnötig

Diese Funktion müssen Sie verändern:

DEF FN Fakultaet# (X#)
	LOCAL R#
	IF X# = l# THEN
		R# = l#
	ELSE
		R# =  Funktion Fakultaet# (X# - l#)
	ENDIF
RETURN R#

Generell sollte der Ausdruck hinter dem RETURN nur aus einer Rückgabevariable bestehen und keine Funktionsaufrufe enthalten. Bei Prozeduren mit mehreren RETURNs gilt das gleiche. Hier wird man allerdings meist mit EXIT arbeiten, so daß keine Schwierigkeiten entstehen. Ebenfalls unzulässig:

DEF PROC Show_Text (X, Y, Text$, Attribut)
	IF 0 THEN DEF PROC Show_Text ( X, Y, Text$):
	LOCAL Attribut = 0
	...
RETURN

Dies ist eine Prozedur mit mehreren Eingängen. Der Aufruf erfolgt wahlweise mit »Attribut«-Parameter. Solche Konstruktionen, die unelegant sind und die Übersicht vermindern, sind in einer Library nicht erlaubt. Sie sollten diese durch folgende ersetzen:

DEF PROC Show_Text ( X, Y, Text$, Attribut )
	...
RETURN
DEF PROC Show_Text ( X, Y, Text$ )
	Show_Text ( X, Y, Text$, 0 )
RETURN

Aufrufe von Unterprogrammen über GOSUB gehören in die graue Basic-Steinzeit und haben in einer Library nichts verloren. Weiterhin ist es notwendig, daß jede Library mit einer Prozedur beginnt. Diese muß unmittelbar zu Beginn des Quelltextes stehen, es darf also kein Kommentar, keine Einrückung oder Leerzeile davor stehen. Diese »namensgebende« Prozedur benötigt der Interpreter, um festzustellen, ob die Library bereits geladen ist. Nach dem Befehl »LIBRARY Gem,“C:\GEM.LIB“« lädt der Interpreter die Library nur, wenn die Prozedur »Gem« nicht existiert. Der Name dieser Prozedur erscheint auch bei geladener Library in der »LIBRARY CODE«-Zeile. Sie gibt vereinbarungsgemäß eine Copyrightmeldung mit Versionsnummer und Fertigungsdatum aus. Wenn Sie also bei geladener GEM.LIB im Direktmodus einfach nur GEM <Return> eingeben, erscheint etwa folgender Text: »GEM - Library für OMIKRON.Basic ... Version 1.0x release XX.XX.89«

Wichtig ist: Diese Art »Fehler« - Prozeduren mit mehreren Eingängen, Prozeduren und Funktionen mit mehreren Ausgängen oder auch das Fehlen der ersten Prozedur - erkennt und meldet der Library-Maker. Enthält ein Teil der Library dagegen ein Label (bei GOTO oder RESTORE), das eventuell nicht eingebunden wird, so bleibt dies unentdeckt. Hier muß der Programmierer besondere Sorgfalt walten lassen.

Außerdem darf eine Library keinen MEMORY_BLOCK enthalten. Der Grund hierfür ist leicht einsehbar: Unter Omikron-Basic wird ein MEMORY_BLOCK vom Tokenizer während des Eintippens angelegt. Aus technischen Gründen liegt er jedoch nicht etwa direkt im Basic-Programm (im Codesegment), sondern befindet sich im Variablensegment. Da nun aber die Library bereits tokenisiert vorliegt und die Library selbst kein Variablensegment enthält, ist es beim Laden der Library nicht möglich, einen MEMORY_BLOCK anzulegen. Befehle außerhalb von Prozeduren oder Funktionen sind verboten. Ausnahmen: Einrückungen und Kommentare. »Leere« Strukturen sollten Sie vermeiden. Fügen Sie zwischen Strukturanfang und -ende zumindest einen Doppelpunkt ein. Keine Struktur darf hinter einem einzeiligen IF beginnen und über mehrere Zeilen weitergehen. Falsch ist beispielsweise:

IF COMPILER THEN REPEAT	´
			´ ...
UNTIL B = A	´

Richtig ist dagegen:

IF COMPILER THEN
	REPEAT
		...
	UNTIL B = A
ENDIF

Zulässig ist auch eine einzeilige Struktur hinter »einzeiligem IF«:

IF COMPILER THEN WHILE PEEK(P)<>0 : P-PEEK(P) : WEND

Die Extension einer Library-Datei sollte immer »LIB« lauten. Eine Library sollte immer eine INIT- und eine EXIT-PROC haben. Hier werden globale Konstanten für Hauptprogramm und Library definiert und Felder dimensioniert. Alle internen Variablen-, Prozedur-, Funktions- oder Label-Namen, die von außen für den Benutzer der Library nicht zugänglich sind, sollten mit einer speziellen Endung, die mit »« aufhört, gekennzeichnet sein. Alle Namen, die auf »«enden, sind auch grundsätzlich nicht in der Liste der globalen Größen aufgeführt (siehe unten). Darf der Anwender die global benutzten Variablen oder Felder von außen nutzen, so wählen Sie die Namen so, daß Überschneidungen mit dem Hauptprogramm unwahrscheinlich sind. Grundsätzlich behandelt der Interpreter alle globalen Größen innerhalb der Library genauso wie diejenigen außerhalb. Wenn also Zuweisungen wie »Fehler = True« in der Library vorkommen und sich jemand den Spaß erlaubt, »True« gleich Null zu setzen, dann brauchen Sie sich nicht über Fehlfunktionen des Programms zu wundern. Geben Sie allen notwendigen globalen Größen einen klangvollen Namen und dokumentieren Sie sie genau - auch ihren Verwendungszweck. Auf der TOS-Disk finden Sie eine eingeschränkt lauffähige Version des Generatorprogramms, mit dem Sie Ihre eigenen Libraries für Omikron-Basic erzeugen. Die Einschränkung besteht darin, daß Ihre Library nicht mehr als 100 verschiedene Bezeichner (also Prozeduren, Funktionen und Variablen zusammen) enthalten darf. Außerdem sind einige Beispiele gespeichert, die den Aufbau einer Library verdeutlichen.

Gehen Sie nun an die Entwicklung eigener Libraries zum Omikron-Basic. Vielleicht werden die vom Hersteller selbst vertriebenen Libraries bald eine rege Konkurrenz von privaten Anbietern, eventuell Public Domain, erfahren. (ah)


Stefan Rinke
Aus: TOS 02 / 1991, Seite 96

Links

Copyright-Bestimmungen: siehe Über diese Seite