\documentclass[a4paper]{article} \usepackage[utf-8]{inputenc} \usepackage[german]{babel} \usepackage{url} \usepackage{alltt} \usepackage{multicol} \renewcommand{\reftextbefore}{auf der vorherigen Seite} \renewcommand{\reftextfacebefore}{auf der gegenüberliegenden Seite} \renewcommand{\reftextafter}{auf der nächsten Seite} \renewcommand{\reftextfaceafter}{auf der gegenüberliegenden Seite} \renewcommand{\figurename}{Abbildung} \title{Ein einfaches Quellcode–Bibiliothekssystem} \ifx\shorttitle\undefined\else \shorttitle{Quellcode–Bibliothek} \fi \author{Ulrich Hoffmann} \begin{document} \maketitle \begin{abstract} {\bf Zusammenfassung:} Der ANS--Forth--Standard ermutigt uns dazu, portablen Code zu schreiben. Zum ersten Mal in der Forth--Geschichte ist es möglich, substantielle Mengen Software zu entwickeln, die nicht nur auf einem einzigen Forth--System laufen kann. Wiederverwendbare Teile von Applikationen können in portablen Bibliotheken zusammengefasst werden. Diese Möglichkeit, portable Bibliotheken schreiben zu können, wirft die Frage auf, wie sie auf einfache und dennoch effiziente Weise verwaltet werden können. Dieser Artikel beschreibt ein einfaches System, Bibliotheken auf Quellcode--Ebene zu behandeln. Seine Einfachheit macht es leicht verständlich und gut handhabbar. Dieses Bibiothekssystem ist seit 1995 auf unterschiedlichen ANS--Forth--Systemen im Einsatz und dient als Grundlage einer kontinuierlich wachsenden Bibliothek von Standard--Forth--Definitionen. {\bf Schlüsselwörter}: Quellcode, Bibliothek, Interpretierer, bedingte Übersetzung \end{abstract} \begin{multicols}{2} \section{Einleitung} Ein Vorteil den der ANS--Forth--Standard der Forth--Gemeinde bringt, ist die Möglichkeit, Algorithmen in einer wohldefinierten und portablen Notation [1] formulieren zu können. Dies ermöglicht, Standard--Bibliotheken anzulegen, die auf einem breiten Spektrum Standard--konformer Forth--Systeme benutzt werden können. Solche Bibliotheken können die unterschiedlichsten Arten von Funktionalität bereitstellen. Sie können numerische Algorithmen enthalten, wie etwa im \emph{Scientific Forth Library}--Projekt [2], häufig verwendete Datenstrukturen, wie beispielsweise in der \emph{FFL} [7] oder anderes. Allerdings ist zu beachten, dass der ANS--Forth--Begriff des \emph{Standard--Programm}\/s Portabilität nur auf Quellcode--Ebene definiert. Derzeit gibt es keine Ansätze, die Portabilität auf Zwischencode-- oder Maschinencode--Ebene zu behandeln. Auch dieser Artikel konzentriert sich auf die Verwaltung von Quellcode--Bibliotheken. \section{Der Kopier--Ansatz} \begin{figure*}[t] \begin{center} \begin{minipage}[t]{0.6\textwidth} {\bf Format für die gesamte Bibliothek:}\medskip \begin{quote} {\sf } {\sf } \verb| CR .( looking for ) 2DUP TYPE SPACE| {\sf } \verb|CR TYPE .( NOT provided! ) QUIT| \end{quote} \bigskip {\bf Format für jedes einzelne Fragment:}\medskip \begin{quote} \verb|desires| {\sf } \verb|[IF] \\| \verb|-----------| \hspace{1cm}{\sf } \verb|\\ [THEN]| \end{quote} \end{minipage}\medskip \caption{\label{qcl:layout}Vorschlag für die Formatierung von Quellcode--Bibliotheken mit sequentieller Suche} \end{center} \end{figure*} Der einfachste Ansatz, Applikationen auf Basis von Quellcode--Bibliotheken zu bauen, besteht darin, den Quellcode der Bibliothek zur Konstruktionszeit der Applikation vor dem Applikations--Quellcode textuell einzufügen. (Man nennt ihn auch \emph{Call--by--Editor}--Ansatz.) Dieser Ansatz hat jedoch einen Nachteil. Der Bibliotheks--Quellcode wird für jede Applikation kopiert. Aus Wartungsgesichtspunkten ist das nicht wünschenswert, weil Verbesserungen im Bibliothekscode in jeder Kopie vorgenommen werden müssen. Höchstwahrscheinlich werden die kopierten Bibliotheken auseinanderlaufen und die Gesamtqualität der Bibliothek wird sich nicht wirklich verbessern.\footnote{Es kann durchaus sinnvoll sein, diesen Ansatz zu wählen, etwa wenn die verschiedenen Konfigurationen für individuell angepasste Geräte verwaltet werden sollen. Jedes Gerät wird dann mit dem vollständigen Quellcode der auf ihm laufenden Software --- inklusive der Bibliotheken im jeweiligen Wartungszustand --- ausgestattet.} \begin{center} \includegraphics[width=0.9\columnwidth]{2008-02/QB-copy-app}\\ \vspace*{-3mm} \label{QB:Figure1}Die Bibliothek zur Konstruktionszeit der Applikation ganz oder teilweise kopieren \end{center} Dieser Nachteil kann gelöst werden, indem man den Bibliotheks--Quellcode eben nicht zur Konstruktionszeit der Applikation einkopiert, sondern ihn mit \verb|INCLUDE| einfügt, also den Kopierprozess effektiv auf die Übersetzungszeit verschiebt. Dies zentralisiert den Bibliotheks--Code an einer Stelle und ermöglicht eine angemessene Wartung (vermutlich mit Hilfe einer Versionsverwaltung). \vspace*{-7mm} \begin{center} \begin{minipage}[t]{0.9\columnwidth} \includegraphics[width=\textwidth]{2008-02/QB-include}\\[-10pt] \label{QB:Figure2}Auf die Bibliothek mit \verb|INCLUDE| zugreifen \end{minipage} \end{center} \newpage Der \verb|INCLUDE|--Ansatz hat allerdings einen anderen Nachteil. Er fügt immer den gesamten Bibliotheks--Code in den Applikations--Code ein, obwohl die meisten Applikationen nur einen Teil der gesamten Bibliothek benötigen. Wir könnten diesen Nachteil überwinden, indem wir ausgewählte Fragmente der Bibliothek in unseren Applikations--Code kopieren --- aber dann sind wir wieder beim obigen Kopier--Ansatz mit seinem Nachteil. \section{Der selektive \texttt{INCLUDE}--Ansatz} Die Schlüsselidee, dieses Bibliothekverwaltungs--Problem zu überwinden, ist, selektiv nur Fragmente der Bibliothek einzufügen. Dann enthält jede Applikation nur die wirklich von ihr benötigten Teile. Und --- der Bibliotheks--Code selbst steht an einer zentralen Stelle und kann einfach gewartet werden. \begin{center} \includegraphics[width=0.9\columnwidth]{2008-02/QB-selectiv}\\ \label{QB:Figure3}Selektiv auf die Bibliothek zugreifen \end{center} Obwohl ein solcher Mechanismus in ANS--Forth nicht standardisiert ist, kann er leicht hinzugefügt werden. Eine Forth--Eigenschaft hilft hier: die Möglichkeit, benutzerdefinierte Funktionen auszuführen, während man Quellcode lädt. Das erlaubt uns, Text benutzerdefiniert zu verarbeiten --- und das neben dem eigentlichen Laden des Quellcodes. Selektiv Fragmente einer Quellcode--Bibliothek zu laden, ist lediglich eine spezielle Verarbeitung von Text. Wir könnten nun ausgefeilte Bibliotheks--Suchfunktionen definieren, die, im Applikations--Code ausgeführt, die gewünschten Fragmente der Bibliothek finden und sie laden. Das werden wir hier aber nicht tun! Statt dessen machen wir die Bibliothek selbst für die Bibliothekssuche verantwortlich, ähnlich wie bei \emph{Bedingter Übersetzung}\/. Wir definieren zunächst nur die Schnittstelle, über die Applikationsprogramme eine solche intelligente Bibliothek verwenden. Das hat den Vorteil, dass die Bibliothekssuche für jede einzelne Bibliothek individuell optimiert werden kann. \section{Die Bibliotheks--Schnittstelle} Um selektiv ein Fragment einer Quellcode--Bibliothek zu laden, hinterlegt die Applikation einen \emph{Identifikations--String} auf dem Datenstack und lädt dann den gesamten Bibliotheks--Quellcode. Anhand des Identifikations--Strings kann die Bibliothek dann entscheiden, welches Fragment die Applikation benötigt, und es selektiv laden. \begin{large} \begin{center} \texttt{( c-addr u -{}- )} \end{center} \end{large} Da die Bibliothek die Bibliothekssuche selbst vornimmt, können wir speziell zugeschnittene Such--Algorithmen je nach Bibliothek einsetzen, die so einfach oder so effizient sein können, wie benötigt (sequentielle, binäre, hash--Suche, usw.). Die einzige Anforderung an die Bibliothek ist, die Schnittstelle zu befriedigen und den übergebenen Identifikations--String zu konsumieren. Weil Teile der Bibliothek abhängig von anderen Bibliotheken (oder anderen Teilen derselben Bibliothek) sein werden, muss es möglich sein, Bibliotheken rekursiv und verschachtelt zu verwenden. Das ermöglicht die Konstruktion von Bibliotheks--Hierarchien. \section{Eine Beispiel--Implementierung} Die hier präsentierte Idee nimmt an, dass Quellcode in sequentiellen Files gehalten wird. Obwohl das Bibliotheks--System nicht mit Quellcode--Blöcken getestet wurde, sollte eine entsprechende Anpassung problemlos möglich sein. Die einfachste Implementierung wäre wohl, den Identifikations--String direkt auf den Datenstack zu legen und dann mit \verb|INCLUDE| die Bibliothek zu laden: \begin{small} \verb| S" " INCLUDED| \end{small} Aber --- hier gibt es ein Problem, denn \verb|S"| legt den String, den es parsed, an einer temporären Stelle im Speicher ab\footnote{ Siehe [1] 11.6.1.2165 Definition von \texttt{S"} im \emph{Optional File Access word set}.} und ein Standard--Forth--System darf mit einem zweiten \verb|S"| diesen temporären Puffer überschreiben. Es ist also nötig, den Identifikations--Stings in einem sicheren Bereich zu speichern. Wir versehen das mit ein wenig syntaktischem Zucker, der uns eine einfachere Handhabung erlaubt, und schlagen folgende Syntax vor, mit der eine Applikation auf eine Quellcode--Bibliothek zugreift:\footnote{Die Verwendung des Namens \texttt{REQUIRE} haben wir auf der Forth--Tagung 2008 heftig diskutiert, da sie in Konflikt mit dem Forth--2000x--Wort gleichen Namens steht. Über Vorschläge für eine konfliktfreie, eingängige Benennung der vorgestellten Worte wäre ich sehr dankbar.} \begin{small} \{ \verb|FROM| {\sf }\\ \hspace*{3mm}\{ \verb|REQUIRE| {\sf } \}*\\ \}* \end{small} Es gibt einen gewissen \emph{Overhead}\/ durch die Definition dieser Worte, die ja jede Applikation enthalten muss. Auf Systemen, die transiente Definitionen erlauben, kann dieser Overhead vermieden werden. Der Quellcode in Listing 3 auf Seite \pageref{QL:listing3} definiert das einfache Bibliotheks--System. \section{Bibliotheks--Formatierungs--Vorschlag} Der Bibliotheks--Quellcode führt selbst die Suche der zu ladenden Fragmenten durch. Es ist sinnvoll, ihn je nach Suchverfahren in geeigneter, spezieller Weise zu formatieren. Der Formatierungs--Vorschlag in Abbildung \vref{qcl:layout} zeigt, wie man den Bibliotheks--Quellcode für eine sequentielle Suche formatieren kann. Zunächst gibt es den Bibliotheks--Kopf, der den Namen und die Version sowie das Veröffentlichungsdatum der Bibliothek enthält. Dann folgt ein wenig Code, um den Identifikations--String zu verarbeiten. Typischerweise wird dieser Teil ermitteln, ob die betreffenden Definitionen bereits geladen wurden, und in diesem Fall die Bibliotheks--Suche vermeiden. Anschließend wird für jedes Fragment festgestellt, ob es geladen werden soll. Das erste zutreffende Fragment wird geladen und dann das Laden des restlichen Files mit \verb|\\| abgebrochen. Kann schließlich kein geeignetes Fragment gefunden werden, wird eine Meldung, z.~B.\ \verb|not provided|, ausgegeben und das selektive Laden bricht mit einem Fehler ab. \section{Verwandte Arbeiten} Der erste bekannte Versuch zur modularen Programmierung in Forth wurde 1980 von D.\ Val Schorre [6] unternommen. Sein Ansatz entfernt die Namen interner Worte durch \emph{Auslinken }aus dem Forth--Dictionary und beschränkt auf diese Weise ihre Benutzung außerhalb des Moduls. Diese Idee wurde von mir in [4] verfeinert und entfernt --- im Kontext moderner Forth--Systeme, die temporäre Namen erlauben --- die Namen der internen Worte vollständig. Die von S.\ Pelc und N.\ Smith beschriebene DOS--basierte Forth--Implementierung \emph{Modular Forth} erlaubt das Linken von Modulen auf Objektcode--Ebene [3]. Im Gegensatz zu dieser Arbeit, bei der die Applikation selbst ein (spezielles) Modul ist und also Applikations--Programme und Systemmodule ähnlich strukturiert sind, berücksichtigt der hier vorgestellte Ansatz die unterschiedlichen Rollen, die Systembibliotheken und Anwendungsprogramme im Software--Konstruktionsprozess haben. Als Konsequenz sind hier Applikations--Programme und Systembibliotheken unterschiedlich strukturiert. Einige frühe Arbeiten über Quellcode--Bibliotheken wurden 1985 von J.\ James [5] durchgeführt. \section{Zukünftige Entwicklungen} Ich habe begonnen, Bibliotheks--relevanten Quellcode, der in vielen Forth--Dialekten formuliert war, Schritt um Schritt in ANS--Forth umzuwandeln, um eine Basis für künftige Applikationen zu schaffen. Mit mittlerer Priorität entstehen auf diese Weise ANS--Forth--Standard--Bibliotheken für Stringverarbeitung, Speicherverwaltung, Zustandsautomaten, Datenstrukturen, Kommunikation und anderes. \section{Ausblick} Das vorgeschlagene einfache Quellcode--Bibliotheks\-system ist bei mir seit vielen Jahren in Benutzung und hat sich als angenehm zu benutzen herausgestellt. Um eine Standard--Anwendung zu konstruieren, kann ich sie ganz einfach auf vorhandene Komponenten aufbauen, indem ich lediglich ihre Abhängigkeiten benenne. Die Programmierung von Standard--Anwendungen wird so viel leichter. Um allerdings Standard--Forth--Code zu \emph{veröffentlichen}, muss ein zusätzlicher Schritt gemacht werden, denn die verwendeten Bibliotheks--Routinen sind anderen eventuell gar nicht bekannt. Es ist dann nötig, die Verweise auf Bibliotheks--Routinen durch die geladenen Definitionen zu ersetzen. Diese zusätzliche Arbeit bei der Veröffentlichung von Programmen wird durch die Einfachheit der Entwicklung mit Bibliotheken mehr als kompensiert. \end{multicols} \section{Literatur} \begin{tabular}{lp{16cm}} {[1]} & dpANS: \emph{Draft proposed American National Standard for Information Systems -- Programming Languages -- Forth} Secretariat Computer and Business Equipment Manufacturers Association, American National Standards Institute, Inc., 1993 \\ {[2]} & \emph{Scientific Forth Library} der aktuelle Status kann unter \url{http://www.taygeta.com/fsl/sciforth.html} eingesehen werden.\\ {[3]} & \emph{Modular Forth: Import, Export and Linking}, Stephen Pelc and Neil Smith, 1986 FORML Conference Proceedings, Forth Interest Group, San Jose, 1987\\ {[4]} & \emph{Module Forth}, Ulrich Hoffmann, 1989 FORML Conference Proceedings, Forth Interest Group, San Jose, 1990 \\ {[5]} & \emph{A Forth Component Library for Off--The--Shelf Distribution of Modules}, John S. James, 1985 FORML Conference Proceedings, Forth Interest Group, San Jose, 1986\\ {[6]} & \emph{Adding MODULEs to Forth}, D. Val Schorre, 1980 FORML Conference Proceedings, Forth Interest Group, San Jose, 1981\\ {[7]} & \emph{Forth Foundation Library}, \url{http://ffl.dvoudheusden.net/} \end{tabular} \vfill \section{Glossar} {\bf In Applikations--Programmen, um auf die Bibliothek zuzugreifen:}\medskip \begin{tabular}{lp{14cm}} \texttt{from} & \verb|( ccc -- )|\\ & {\bf from} wird in der Weise {\texttt{from }{\sf }} verwendet, um die aktuelle Bibliothek auf den Wert {\sf } zu setzen. {\sf } ist der Name des Files, das den Bibliotheks--Quellcode enthält.\\ \\ \texttt{required} & \verb|( c-addr len -- )|\\ & {\bf required} wird verwendet, um die durch den Identifikations--String {\sf c-addr len} bezeichnete Definition (und ihre abhängigen Definitionen) aus der aktuellen Bibliothek zu laden. Der Identifikations--String ist typischerweise der Name einer Definition.\\ \\ \texttt{require} & \verb|( ccc -- )|\\ & {\bf require} ist eine Präfix--Version von {\bf required}. Sie wird in der folgenden Form verwendet:\\ &\texttt{require} {\sf }. \end{tabular} \bigskip {\bf In Bibliotheken:}\medskip \begin{tabular}{lp{14cm}} {\verb|\\|} & \verb|( -- )| immediate\\ &\verb|\\| überspringt allen verbleibenden Quellcode im aktuell geladenen File.\\ \\ \texttt{desired}& \verb"( c-addr1 len1 c-addr2 len2 -- c-addr1 len1 false | true )"\\ & {\bf desired} vergleicht die beiden durch {\sf c-addr1 len1} und {\sf c-addr2 len2} gegebenen Strings und liefert {\sf true}, wenn beide Strings die gleichen Zeichen in der gleichen Reihenfolge enthalten. Wenn die Strings nicht gleich sind, liefert es {\sf c-addr1 len1} und {\sf false}.\footnote{}\\ \\ \texttt{desires} & \verb|( ccc c-addr len -- c-addr len false | true )|\\ &{\bf desires} ist eine Präfix--Version von {\bf desired}. Es wird in folgender Form verwendet:\\ &\texttt{desires} {\sf }\\ \end{tabular} \vfill \footnotetext{Es ist also ähnlich zum Wort {\bf case?}, das folgendermaßen definiert werden kann: \\ \verb": case? ( x1 x2 -- x1 false | true ) OVER = DUP IF NIP THEN ;"\\ Man beachte aber den Unterschied, dass {\bf desired} Strings und keine Zell--Werte vergleicht.} \newpage \section{Listing 1: Eine einfache Bibliothek mit sequentieller Suche} \begin{quote} \listinginput[1]{1}{2008-02/sample.lib} \end{quote} \section{Listing 2: Eine Beispiel--Applikation} \begin{quote} \listinginput[1]{1}{2008-02/appl.f} \end{quote} \section{Listing 3: Quellcode des einfachen Quellcode--Bibliothekssystems} \label{QL:listing3} \begin{quote} \listinginput[1]{1}{2008-02/library.ans} \end{quote} \end{document}