KAPITEL 3 Generator

Bei dem Generator handelt es sich um einen Compiler zur Übersetzung von Seiten mit erweitertem Befehlssatz in normales Html und zur Einbindung dieser Html- Seiten in die ® Arbeitsumgebung. Durch den Einsatz des Generators wird die Erstellung der Lerninhalte erheblich erleichtert und beschleunigt. Dies bewerkstelligt der Generator, indem er die Inhalte von einer einfach herzustellenden und übersichtlichen Form, die unabhängig von dem Generator erstellt wird, in eine an die Arbeitsumgebung angepasste Form bringt. Außerdem generiert er das Inhalts- und Stichwortverzeichnis. Ein weiteres Feature ist, dass der Generator selbst definierte Erweiterungen in Html übersetzt und somit die Gestaltung der Lerninhalte flexibel an sich ändernde Bedürfnisse angepasst wird.

Rahmenbedingungen

Zielgruppe

Der Generator soll von Anwendern bedient werden, die nur grundlegende Kenntnisse im Umgang mit Computern und MS-Windows haben. Die Benutzer haben auch grundlegende Kenntnisse in der Erstellung von Html Dokumenten mit Hilfe eines Html Editors. Der Schwerpunkt der Arbeit soll in der Erstellung neuer Inhalte liegen und nicht in der Gestaltung und dem Design.

Plattform

Um einen einfachen und unkomplizierten Einsatz gewährleisten zu können, muss der Generator unter einer grafischen Oberfläche laufen. Da die Übersetzung von umfangreichen Inhalten sehr rechenintensiv ist, ist der Einsatz eines leistungsstarken PC's zu empfehlen. Sobald mehrere Anwender parallel an den Inhalten arbeiten, ist eine Netzwerkanbindung von Vorteil. Dadurch kann auf ein zentral definiertes Layout zurückgegriffen werden, und eventuelle Änderungen fließen sofort in alle Entwicklungen ein.

Funktionen

Die Funktionen des Generators gliedern sich in zwei Teile:

Die Übersetzung von Erweiterungen in Html ist erforderlich, da die Inhalte in einem einfachen übersichtlichen Stil geschrieben werden sollen. Trotzdem sollen beim Layout die gestalterischen Möglichkeiten von Html zur Verfügung stehen. Es muss also zusätzliche, einfach zu lernende Elemente geben, welche die Gestaltung beeinflussen. Diese Elemente müssen jedoch in standart-Html übersetzt werden, damit die Inhalte von normalen Browsern angezeigt werden können. Die Übersetzung hat außerdem den Vorteil, dass z.B. standard-Kopf- und Fußzeilen eingesetzt werden können, ohne dass dies bei der Erstellung beachtet werden muss.

Das Einbinden der Inhalte in die Arbeitsumgebung ist die zweite Aufgabe. Diese Funktion erstellt das Inhalts- und Stichwortverzeichnis, generiert die ® Verlinkung zu den anderen Seiten und passt das Layout an die Arbeitsumgebung an.

Folgerungen

Zusammenfassend ergibt sich aus den beschriebenen Kriterien Folgendes:

Um eine Einarbeitungszeit bei der Benutzung des Generators zu vermeiden, sollten die Erweiterungen Html-Syntax haben. Außerdem sollte der Generator über ein grafisches Benutzerinterface verfügen und nur mit grundlegenden Einstelloptionen ausgestattet sein. Da die Anwender unter MS-Windows arbeiten und sich mit dieser Oberfläche auskennen, sollte auch der Generator unter MS-Windows laufen. Die Erstellung der Inhalte sollte weiterhin unter Zuhilfenahme eines Html-Editors möglich sein und wenig von der bisherigen Erstellung von Webseiten abweichen. Abweichungen von der üblichen Erstellung von Webseiten sollten sich weitgehend auf Erleichterungen beschränken und den Anwender nicht mit zusätzlichen Regeln oder Einschränkungen belasten. Es sollte möglich sein, die erstellten Inhalte auch schon vor der Übersetzung zu betrachten um eine grobe Vorstellung von dem endgültigen Ergebnis zu erhalten, was psychologisch wichtig ist.

Die Positionierung und Farbgebung der Inhalte sollte so weit wie möglich automatisch geschehen und -sofern überhaupt nötig- einfach durch Schlüsselworte beeinflusst werden. Da es denkbar ist, dass das Design der Seiten geändert werden soll, bietet es sich, an die Erweiterung des Html-Befehlssatzes nicht statisch in den Generator einzubinden, sondern dynamisch, um eine nachträgliche Änderung zu ermöglichen. Eine dynamische Einbindung würde auch die Erstellung neuer Erweiterungen vereinfachen.

Es wäre auch vorteilhaft, wenn die Einbindung der Inhalte in die Arbeitsumgebung über Html-erweiterungen gesteuert werden könnte, was den Generator simpel halten und universell einsetzbar machen würde. Dadurch könnte der Generator leicht an andere Projekte angepasst werden.

Entwurf

Die Erstellung des Generators gliedert sich in drei Teile:

Alle Teile sollten eigenständig sein, um eine leichte Anpassung an geänderte Bedürfnisse zu gewährleisten. Zu beachten ist das die Benutzerschnittstelle über eine festzulegende Schnittstelle mit dem Übersetzer verbunden werden muss. Ebenso ist die Form der Erweiterung (Syntax) festzulegen. Das Folgende Diagramm ( Siehe Der Generator und seine Elemente. ) zeigt das Zusammenspiel der einzelteile.

Der Generator und seine Elemente

Benutzerschnittstelle

Die Benutzerschnittstelle ist der Teil des Generators mit dem der Anwender in Berührung kommt. Daher muss sie übersichtlich und leicht bedienbar sein. Sie muss über ein grafisches Benutzerinterface verfügen, soll nur grundlegende Einstelloptionen bieten und Statusmeldungen zum Fortschritt des Übersetzungsvorganges ausgeben.

Bedienelemente

Aus den Anforderungen ergibt sich, dass die Benutzerschnittstelle über folgende Elemente verfügen muss:

  • Anzeige und Auswahl der zu übersetzenden Datei
  • Ausgabe des Übersetzungsfortschritts
  • Ausgabe von Statusmeldungen (Warnungen, Fehler)
  • Button, der den Übersetzungsvorgang einleitet / stoppt

Die genannten Elemente werden während der Arbeit mit dem Generator immer wieder benötigt und sollten daher auch ständig sichtbar bzw. einfach erreichbar sein.

Zusätzlich wären noch folgende Bedienelemente wünschenswert:

  • Festlegen der Arbeitsverzeichnisse
  • Laden und Speichern der aktuellen Einstellungen
  • Einstellen des Debuglevels

Da die zuletzt genannten Elemente jedoch nur gelegentlich benötigt werden, ist es sinnvoll, dass diese nicht ständig sichtbar sind.

Erweiterter Befehlssatz

Dieser Teil beschreibt in welcher Art Erweiterungen in die Dokumente einfließen und wie diese realisiert werden.

Syntax

Wie bereits festgestellt sollen sich die Erweiterungen aus der Sicht des Entwicklers möglichst nahtlos in die gewohnte Erstellung von Web-Sites integrieren. Um dies zu gewährleisten, ist es sinnvoll, dass Erweiterungen die Syntax von Html verwenden. Erweiterungen sind also Tags, deren Bedeutung selbst festgelegt wird. Wenn Erweiterungen syntaktisch identisch mit der übrigen Website sind, hat das auch noch den Vorteil, dass sich auch Tags, die in Html bekannt sind, mit einer neuen oder erweiterten Bedeutung belegen lassen. Das ist dann sinnvoll, wenn z.B. der Body-Tag grundsätzlich eine bestimmte Hintergrundfarbe erhalten soll, ohne dass dieses explizit angegeben wird. Wenn nun also im Quellcode der Tag: <body> auftaucht, wird dieser vom Generator durch den Tag <body bgcolor="#00FF00"> ersetzt.

Erstellung

Zu bedenken ist, dass die Parameter der Tags bei der Definition von Erweiterungen zur Verfügung stehen müssen. Zusätzlich sollen auch Fallunterscheidungen auf Grund der Parameter möglich sein. Wenn beispielsweise bei einem Tag ein bestimmter Parameter angegeben ist, ein soll zusätzlicher Html-Code eingefügt werden. Als Beispiel könnte bei dem Image-Tag ein weiteres Bild angegeben werden, welches bei einem Rollover angezeigt werden soll. Ist dieser zusätzliche Parameter nicht angegeben, soll auch zu dem Image-Tag kein zusätzlicher Code für den Rollover eingebunden werden.

Häufig ist es auch erforderlich, Zustände zu speichern oder einfache Rechnungen durchzuführen, die schon während des Übersetzungsvorganges gebraucht werden. Auch das Ausgeben von Statusinformationen während des Übersetzungsvorganges gehört zu den interessanten Hilfsmitteln.

Die in diesem Abschnitt geforderten Eigenschaften der Erweiterungen gehen deutlich über ein einfaches Austauschen von Tags gegen Html-Code hinaus. Vielmehr erinnern sie an die Eigenschaften von JavaScript, nur mit der Einschränkung, dass diese Funktionalität schon beim Übersetzen benötigt wird und nicht erst beim Betrachten der eigentlichen Seite. Es wird also, neben der einfachen Benutzung von Html, eine Makrosprache benötigt, die Einfluss auf den Übersetzungsvorgang nehmen kann.

Makrosprache

Makros, die in Erweiterungen eingesetzt werden, müssen eine Syntax haben, die leicht zu erlernen ist. Unter Berücksichtigung des Einsatzortes bietet es sich an, die Syntax soweit möglich an der von JavaScript zu orientieren. Dies beinhaltet die arithmetischen Operationen sowie die Ablaufkontrolle. Da es sich bei den Makros voraussichtlich um kleine Aufgaben handelt, ist es nicht erforderlich, die Definition eigener Funktionen zu unterstützen.

Der Geltungsbereich von Variablen muss an die Aufgabenstellung angepasst sein. Im Detail bedeutet dies, dass Parameter der Tags als Variablen zur Verfügung stehen müssen. Zusätzlich müssen Variablen ebenso wie die Parameter, im Teil der Erweiterungen, der durch Html beschrieben wird, zur Verfügung stehen. Variablen müssen während des gesamten Übersetzungsvorganges ihren Wert behalten, damit sie auch für Makros anderer Erweiterungen zur Verfügung stehen. Variablen müssen auch dann noch Ihren Wert behalten, wenn weitere Dokumente übersetzt werden.

Speicherung

Nun bleibt noch die Frage zu klären, wie die Erweiterungen gespeichert werden. Eine Möglichkeit wäre, die Erweiterungen fest in den Übersetzer einzubauen. Dies scheidet allerdings von vornherein aus, da es zu unflexibel ist. Eine bessere Möglichkeit besteht darin, alle Definitionen in einer Datei abzulegen, in der die einzelnen Erweiterungen durch Schlüsselworte eingeleitet und benannt werden. Als drittes bleibt noch die Möglichkeit, für jeden Tag eine eigene Datei anzulegen. Ich habe mich für die letzte Variante aus folgenden Gründen entschieden:

  • Es ist leicht, einen Überblick über die bereits existierenden Erweiterungen zu behalten
  • Einzelne Erweiterungen lassen sich ohne großen Aufwand aktivieren und deaktivieren
  • Es ist einfach, eine individuelle Liste von Erweiterungen zu erstellen
  • Ein Fehler in einer Erweiterung hat keine Auswirkung auf andere Erweiterungen
  • Fehler bei der Erstellung von Erweiterungen lassen sich leicht eingrenzen

Übersetzer

Dieser Teil des Generators soll die eigentliche Übersetzungsaufgabe erledigen. Dies geschiet, indem er Erweiterungen gegen deren entsprechenden Html-Code austauscht. Der Benutzer kommt mit dem Übersetzer nicht direkt in Kontakt, sondern bedient diesen lediglich über die Benutzerschnittstelle.

Da der Übersetzer jedoch ein eigenständiges Modul sein soll, ist es vorteilhaft wenn dieses auch aus der Kommandozeile heraus aufgerufen werden kann um ihn besser testen zu können.

Funktionen

Aus den Anforderungen an den Generator und insbesondere an die Erweiterungen ergeben sich folgende Funktionen für den Übersetzer:

  • Erkennen von Tags, die nicht zum normalen Html-Befehlssatz gehören
  • Erkennen von Tags, die eine neue Bedeutung erhalten haben
  • Austauschen von Tags gegen deren Definition
  • Auswerten der Tagdefinitionen (Erweiterungen)
  • Austauschen der in den Tagdefinitionen enthaltenen Variablen gegen deren Wert
  • Auswerten der in den Tagdefinition enthaltenen Makros
  • Austauschen der Makros gegen deren Ergebnis

All dies sind Aufgaben, wie sie von einem Compiler bzw. einem Interpreter erledigt werden. Und doch sind sie so verschieden, dass es ungeschickt wäre, alle Aufgaben mit einem einzigen Compiler durchführen zu wollen. Besser ist es, dies in Teilbereiche zu unterteilen und jedes für sich zu kompilieren. Dabei ergeben sich folgende Bereiche:

  • Die Dokumente
  • Die Erweiterungen
  • Die Makros

So übernimmt der erste Compiler die Aufgabe, die Dokumente nach Tags zu durchsuchen, die ersetzt werden sollen. Diese Tags werden durch das Ergebnis, das der Erweiterungscompiler liefert, ausgetauscht.

Der zweite Compiler durchsucht die Erweiterungen nach Variabeln und Makros. Die Variablen werden durch ihren Wert ersetzt und die Makros durch das Ergebnis, das der Makrocompiler liefert.

Der dritte Compiler oder besser Interpreter übersetzt die in den Erweiterungen enthaltenen Makros und gibt deren Ergebnis zurück.

Syntax im Detail

Nun ist es als nächstes wichtig, die Syntax der einzelnen Module exakt festzulegen. Hiermit sind die Dokumente gemeint, in denen zusätzliche Tags verwendet werden. Es geht weiterhin um die Syntax der Erweiterungen und der Makros.

Dokumente

Dieser Teil ist der einfachste dieses Kapitels, da hier keine neue Syntax erstellt wird. Alle Erweiterungen werden ausschließlich über die in Html übliche Syntax in die Dokumente integriert. Im Einzelnen heißt das, dass sich jede Erweiterung nach außen hin wie jeder andere Tag auch darstellt.

Das sieht dann so aus:

 
<Tagname  Bezeichner1='Wert1' Bezeichner2='Wert2'> Text </Tagname>

Da es auch möglich sein soll, die Bedeutung von bereits existierenden Tags zu überladen", ist es sinnvoll auch eine Möglichkeit zu bieten auf das Original" zuzugreifen. Um dies zu erreichen wird am Ende des Tags vor der schließenden spitzen Klammer ein Ausrufungszeichen !" angegeben, korrespondierend zu dem Ausrufungszeichen, das einen Kommentar einleitet.

Das sieht dann so aus:

 
<Tagname Bezeichner1='Wert1'!>

Ein ebenfalls nicht unwichtiger Punkt ist die Tatsache, dass im Html die Groß- und Kleinschreibung nicht relevant ist. Dies soll auch bei Erweiterungen der Fall sein damit es nicht zu unnötigen Fehlern kommt.

Erweiterungen

Erweiterungen sollen eine Syntax haben, die sich kaum von der Syntax der normalen Html-Dokumente unterscheidet. Die wesentlichen Unterschiede sind, dass Erweiterungen keine einleitenden Tags wie z.B. <html> brauchen, dass sie die Variablen erkennen und dass es möglich ist, Makros einzufügen.

Eine Erweiterung enthält in seiner einfachsten Form nur den Html-Code, der anstelle des Tags in das Dokument eingesetzt wird.

Sollen nun Variablen in einer Erweiterung eingefügt werden, so weicht dies von der üblichen Html-Syntax ab. Variablen werden benötigt, wenn z.B. einer der übergebenen Parameter benutzt werden soll. Diese Parameter werden nämlich in Variablen gespeichert. Wie im einzelnen Variablen definiert sind und welchen Geltungsbereich sie haben wird im nächsten Abschnitt erläutert. Vorab nur soviel: Variablen zeichnen sich durch ein führendes Dollarzeichen $" aus. Durch dieses werden sie auch innerhalb der Erweiterungen angesprochen. Taucht an beliebiger Stelle in einer Erweiterung ein Dollarzeichen gefolgt von einem Bezeichner (der Name einer Variable) auf, so wird dieser durch den Wert der Variable ersetzt. Das Ende eines Variablenbezeichners wird durch ein Zeichen angezeigt, das für diesen nicht zulässig ist.

Die Verwendung von Variablen könnte dann folgendermaßen aussehen:

 
<a href="$adresse">$text</a>

Zum Übersetzungszeitpunkt werden die Variablen $adresse und $text durch deren Inhalt ersetzt.

Neben den Variablen gibt es noch einen weiteren Zusatz, der vom üblichen Html abweicht. Gemeint ist die Verwendung von Makros. Wie Makros definiert sind, wird im folgenden Abschnitt erklärt. Eingefügt werden sie durch das einleitende Tag <Makro> und abgeschlossen durch das Tag </Makro> . Alles was zwischen diesen beiden Tags steht, ist ein Makro und wird besonders behandelt. Auch hierzu ein Beispiel:

 
<makro>
 
	// Dieses Makro enthält nur diesen Kommentar
 
</makro>

Makros tauchen im fertig übersetzten Dokument nicht mehr auf. Von ihnen bleibt nur das übrig, was innerhalb der Makros explizit als Ausgabe angegeben ist.

Makros

Makros stellen eine eigenständige Programmiersprache dar. Diese orientiert sich stark an JavaScript; das bedeutet, die Syntax ist bei beiden Sprachen gleich, jedoch nicht deren Befehlssatz. Makros kennen Variablen, bei denen der Datentyp automatisch festgelegt wird. Auch müssen Variablen nicht definiert werden, sondern können einfach benutzt werden. Makros kennen einfache arithmetische Operationen und bedingte Ausführung von Code. Des Weiteren kennen Makros Schleifen und einige vorgegebene Funktionen. Jede Anweisung muss durch ein Semikolon abgeschlossen werden. Es ist auch möglich, Anweisungen zu einem Block zusammen zu fassen; das wird realisier indem man mehrere Anweisungen in eine geschweifte Klammer setzt.

Variablen

Einen besonderen Status besitzen die Variablen. Ihr Gültigkeitsbereich soll nicht auf die Ausführung eines Makros beschränkt sein, jedenfalls nicht der Gültigkeitsbereich aller Variablen. Um die Handhabung der Variablen möglichst einfach zu gestalten, ist es erforderlich, Variablen mit unterschiedlichen Gültigkeitsbereich einzuführen.

Um den Grund zu verdeutlichen, bringe ich ein Beispiel. Angenommen, alle Variablen würden während des gesamten Übersetzungsvorganges ihre Gültigkeit behalten. Würde nun in einem Dokument ein neu definiertes Tag aufgerufen, so wären von diesem Moment an die Variablen, die den Parametern des Tags entsprechen, mit Werten belegt. Nehmen wir weiter an, der selbe Tag wird ein weiteres Mal verwendet, diesmal allerdings ohne Angabe von Parametern. Da die Parametervariablen jedoch bereits belegt wurden, würde diese Erweiterung mit den Parametern des ersten Aufrufs übersetzt, was sicher nicht im Interesse des Autors des Dokumentes wäre.

Es wäre auch nicht sinnvoll alle Variablen grundsätzlich nach Beenden eines Tags zu löschen. Dann wäre es beispielsweise nicht möglich mitzuzählen, wie oft ein Tag bereits verwendet wurde oder Werte bis zum Aufruf eines Weiteren Tags zu speichern.

Aus diesen und ähnlichen Gründen brauchen Variablen unterschiedliche Gültigkeitsbereiche. Die unterschiedlichen Gültigkeitsbereiche werden durch unterschiedliche Präfixe der Variablennamen gekennzeichnet. Folgende Einteilung der Gültigkeitsbereiche erweist sich als gut.

  • Wärend des gesamten Übersetzungsvorganges (Präfix $$$)
  • Wärend der Übersetzung eines Dokumentes (Präfix $$)
  • Wärend der Übersetzung einer Erweiterung (Präfix $)
  • Wärend der Übersetzung eines Makros (keine Präfix)

Wichtig ist noch, dass Variablen, die noch nicht mit einem Wert belegt sind, trotzdem verwendet werden dürfen, ohne dass dieses einen Fehler hervorruft. Praktisch ist z.B. eine automatische Vorbelegung mit Null oder einem leerem String.

Arithmetik

Makros sollen alle üblichen arithmetischen Operationen kennen. Ausgenommen sind lediglich die Bitoperationen, da diese nicht erforderlich sind. Es ist jedoch vorgesehen, diese Operationen bei Bedarf ohne großen Aufwand nachzurüsten.

Bedingte Ausführung

Um nicht lediglich einem starren Ablauf folgen zu müssen, sollten Makros auch eine bedingte Ausführung kennen. Hierzu gibt es die in JavaScript übliche if-Anweisung.

Schleifen

Letztlich gibt es noch die Möglichkeit, eine Anweisung innerhalb einer Schleife wiederkehrend auszuführen, um ein Mittel gegen den starr linearen Ablauf zu haben. Als Anweisungen die eine Schleife einleiten, stehen die while-Anweisung und die for-Anweisung zur Verfügung. Die Syntax ist auch hier die selbe wie in JavaScript.

Funktionen

Funktionen können nicht selbst definiert werden, was auch nicht wichtig ist, da Makros lediglich kleine Aufgaben erledigen, die selten über einige wenige Zeilen hinausgehen. Es muss jedoch einige vordefinierte Funktionen geben. Hierzu zählen z.B. eine Funktion zur Rückgabe des Ergebnisses. Dies könnte wie üblich mit Hilfe der return-Funktion geschehen, es erscheint mir jedoch sinnvoller, eine Art print-Funktion einzuführen, die ähnlich wie in Perl den Rückgabewert direkt an die Return-Variable anhängt. Dies erspart dem Nutzer selbst eine solche Variable einzuführen. Außerdem sind alle Rückgabewerte ja nichts anderes als Ausgaben in die Zieldatei.

Des weiteren werden Funktionen zum Aufruf eines bestimmten Teiles des Generators benötigt. Es müssen auch Funktionen zur File-Ein- und Ausgabe zur Verfügung stehen. Vergessen werden dürfen auch nicht Funktionen zur Stringmanipulation.

Welche Funktionen im Einzelnen benötigt werden, und wie diese benannt werden, wird sich während der Erstellung von Erweiterungen zeigen. Aus diesem Grund muss auch eine Möglichkeit gefunden werden, den Befehlssatz an Funktionen einfach aufzustocken, ohne sich jedes Mal von neuem mit der Materie des Compilers auseinandersetzen zu müssen.

Es soll ferner nicht ausgeschlossen sein, den Compiler um eine Möglichkeit zu erweitern, eigene Funktionen innerhalb der Makros zu definieren.

Umsetzung

Nachdem nun die Rahmenbedingungen und der Entwurf abgehandelt wurden, geht es an die Umsetzung. Dieser Teil beschreibt die Feinheiten und die technischen Details.

Umgebung

Bevor ich mich mit dem Generator ausgiebig beschäftige und Entscheidungen zur Umsetzung treffe, gilt es, die Sprache und Arbeitsumgebung, sowie die Werkzeuge mit dessen Hilfe der Generator erstellt wird, festzulegen. Erst danach macht es Sinn, sich um Feinheiten zu kümmern.

Sprache

Als Sprache für die Erstellung des Generators kommen erst einmal alle Sprachen in Betracht, die eine Erstellung von MS-Windows Programmen unterstützen. Ein weiteres wichtiges Kriterium ist die Unterstützung bei der Erstellung von Compilern.

Werkzeuge zur Erstellung von Compilern sind mir vor allem für C++ bekannt, dies allerdings nur unter Linux. Für MS-Windows war es mir nicht möglich, ein solches Werkzeug ausfindig zu machen. Auch ist es problematisch, einen C++-Compiler für Windows zu finden, der als freie Software erhältlich ist. Dies wäre wünschendswert, da die Firma, in dessen Auftrag ich diesen Generator erstelle, über keinen kommerziellen C++-Compiler verfügt, und sich die Anschaffung für eine einmalige Aufgabe nicht rechnet. Alternativ wäre es möglich, den Generator unter Linux zu erstellen und auf einem Linuxrechner, der als Server fungiert, laufen zu lassen. Dies würde allerdings zusätzlichen Hardwareaufwand bedeuten, wäre jedoch durchaus denkbar.

Neben den Werkzeugen zur Erstellung von Compilern in C++ existieren nahezu gleichwertige Werkzeuge für Java. Der Einsatz von Java ist in Bezug auf die Kompatibilität zu MS-Windows unproblematisch, da der Java-Compiler als freie Software für alle gängigen Plattformen erhältlich ist. Vorteilhaft ist dass es ebenfalls freie Entwicklungsplattformen für Java gibt. Zusätzlich wird auch die Erstellung von grafischen Benutzerinterfaces von Java unterstützt.

Da ich mit der Erstellung von Javaprogrammen vertraut bin und alle wesentlichen Bedingungen von dieser Sprache erfüllt werden, habe ich mich auf Java festgelegt. Wesentlich zu diese Entscheidung beigetragen hat auch, dass sich bei der Entwicklung unter Java, die geringsten Schwierigkeiten abzeichnen. Ich werde zur Unterstützung meiner Entwicklung die IDE von Borland, den ® J-Builder, einsetzen, welcher als Freeware erhältlich ist.

Aussichten

Mit der Entscheidung für Java ergeben sich gleich mehrere neue Möglichkeiten. Neben dem Vorteil, dass der Generator dadurch plattformunabhängig ist, und dem Nachteil, dass er etwas langsamer arbeitet, als wenn er unter C++ erstellt würde, gilt es noch Folgendes zu bedenken: Wenn der Generator unter Java erstellt wird, besteht die Möglichkeit, ihn als Kommandozeilenapplikation zu erstellen, er kann ein grafisches Benutzerinterface erhalten und als Applet in einem Browser laufen.

Die Kommandozeilenvariante fällt heraus, da schon vorab festgestellt wurde, dass ein grafisches Benutzerinterface existieren muss.

Die Variante als Applet hat den Vorteil, dass sich der Generator direkt mit der Dokumentation verbinden lässt. Die übersetzten Seiten könnten direkt in einer Vorschau betrachtet werden; auch ohne sie zuvor zu speichern. Auch wäre es so möglich, von jedem Arbeitsplatz aus direkt auf den Generator zuzugreifen, ohne zuvor Java auf dem betreffenden Rechner einzurichten. Die ganze Sache hat jedoch einen Haken. Das Sicherheitskonzept von Java erlaubt es einem Applet nicht, auf lokale Dateien zuzugreifen, jedenfalls nicht im Normalfall. Möchte man dies umgehen, würde es bedeuten, dass im einfachsten Fall auch anderen Programmen der Zugriff auf lokale Dateien gestattet würde.

Bleibt noch die Variante als Applikation mit grafischem Benutzerinterface. Diese Variante hat den Nachteil, dass eine Vorschau der übersetzten Dateien nur sehr aufwendig zu realisieren ist. Hinzu kommt, dass ein ® JRE auf den Rechnern, auf denen der Generator laufen soll, eingerichtet werden muss. Das Einrichten des JRE bedeutet nur einen geringen Aufwand, und der Generator läuft danach wie jede andere Applikation, auch ohne Sicherheitsprobleme.

Ich habe mich entschieden, sowohl die Applet- als auch die Applikationsvariante umzusetzen.

Die Applet Variante ist für Anwender, die den Komfort lieben und keine Bedenken wegen der Sicherheitsrisiken haben.

Die Applikation-Variante ist Vorzuziehen bei Anwendern, die auf Nummer sicher gehen wollen und daher lieber auf die Vorschau verzichten.

Der Compiler Compiler (Werkzeug zur Compilererstellung)

Wie ich bereits festgestellt habe benötige ich ein Werkzeug zur Erstellung der verschiedenen Compiler, die erforderlich sind. Unter Linux für C++ stehen für solche Aufgaben der Lex-Scannergenerator und der Yacc-Parsergenerator zur Verfügung. Auch für Java gibt es inzwischen schon mehrere Implementierungen solcher Werkzeuge. Es ist festzustellen, dass die Benutzung dieser Werkzeuge jedoch von der unter Linux für C++ bekannten abweicht. Nachdem ich mehrere Java-Implementierungen von Lex und Yacc verglichen habe, habe ich mich für die Lösung von C. Scott Ananian 1 entschieden, der bereits existierende Werkzeuge weiterentwickelt hat.

Die von C. Scott Ananian vorgestellten und von mir verwendeten Werkzeuge tragen die Bezeichnungen ® JLex und ® CUP. JLex ist das Java-Äquivalent zu dem bekannten Scannergenerator Lex. Bei CUP handelt es sich um das Java-Äquivalent zu dem bekannten LR(1) Parsergenerator Yacc.

Die ausschlaggebenden Vorteile dieser Java Implementierungen sind:

  • Große Ähnlichkeit mit deren C++ Vorbildern
  • Ein einfach gehaltenes Beispiel zum Einsatz
  • Eine gute und umfassende Dokumentation
Java

Java ist nicht gleich Java. Inzwischen gibt es viele Versionen von Java, wobei jede ihre Vorteile und Nachteile hat. Entscheide ich mich für Java 1.0x, stelle ich damit sicher, dass es auf jedem Rechner läuft. Diese Version fällt jedoch schon daher heraus, da die Werkzeuge JLex und CUP ein Java 1.1x voraussetzen. Der Einsatz des J-Builders übertrifft das ganze noch, da dieser auf Java 1.2x aufbaut. Um zukunftsorientiert zu arbeiten, bestünde noch die Möglichkeit, Java 1.3x einzusetzen. Und es gibt noch eine Versionsvorgabe. Da ich beabsichtige unter anderem eine Applet-Variante zu erstellen, sollte höchstens Java 1.1x zum Einsatz kommen, da die aktuellen Browser lediglich diese Variante umgesetzt haben.

Konflikt

Trotz dieses Versions-Getümmels ist doch zu erkennen, das hier Widersprüche auftauchen. Zum einen soll höchstens Java 1.1x zum Einsatz kommen, um kompatibel zu den aktuellen Browsern zu bleiben, zum anderen kommt Java 1.2x zum Einsatz, sobald ich den J-Builder einsetze.

Lösung

Nun gibt es zwei Möglichkeiten, diesen Konflikt aus dem Weg zu räumen. Es ist möglich, einem Browser eine höhere Java-Version als Plug-In zur Verfügung zu stellen. Damit käme der Browser auch mit denen vom J-Builder erstellten Programmen zurecht, hätte aber den Nachteil, dass die Geschwindigkeitsoptimierung, welche die fest eingebaute ® JVM integriert hat, wegfallen würde.

Die andere Möglichkeit besteht in der Flexibilität von Java. Auch wenn ein Programm mit einem Java 1.2x Compiler übersetzt wurde, so kann es doch mit einer JVM 1.1x ausgeführt werden, sofern lediglich Klassen und Funktionen zum Einsatz kommen, die bereits im Befehlssatz von Java 1.1x enthalten sind.

Wenn also sichergestellt ist, dass der Teil des Generators, der im Webinterface zum Einsatz kommt, mit dem Befehlssatz von Java 1.1x auskommt, so läuft der Generator ohne Update auch im Browser. Bei der Erstellung des grafischen Interfaces der Applikation kann hingegen der volle Umfang von Java 1.2x zum Einsatz kommen, also der J-Builder ohne Einschränkungen benutzt werden.

Übersetzer

Einen Teil des Generators stellt der Übersetzer dar. Der Übersetzer leistet die eigentliche Arbeit, das Austauschen der Erweiterungen gegen Html. Er ist nicht selbständig lauffähig, sondern braucht noch ein Ein-/Ausgabe Interface.

Der Übersetzer teilt sich wie bereits beschrieben in drei grundlegende Teile auf, von denen jedes einen eigenständigen Compiler darstellt.

Im Folgenden werden zunächst die Gemeinsamkeiten der drei Teile beschrieben und anschließend wird auf jedes Teil im Einzelnen eingegangen.

Gemeinsamkeiten

Die Compiler bestehen im Wesentlichen aus einer Scanner-Klasse, die mit JLex erstellt wird, einer Parser-Klasse, die mit CUP erstellt wird und einer Auswertungs-Klasse, welche die Steuerung übernimmt.

Die Steuerung hat die Aufgabe einen Quelltext und die Umgebungsvariablen bereitzustellen sowie die Initialisierung des Compilers vorzunehmen. Der Quelltext wird an den Scanner weitergereicht, der den Text in Tokens zerlegt. Ein Objekt dieses Scanners wird an den Parser übergeben, welcher die einzelnen Tokens vom Scanner abruft. Der Parser hat dann die Aufgabe, die Syntax zu erkennen, zu überprüfen und der Syntax entsprechende Schritte einzuleiten. Das Ergebnis des Parsers wird zum Schluss an die aufrufende Steuerklasse zurückgeliefert, welche dieses wiederum als Ergebnis bereit stellt. Bei dem Parser handelt es sich um einen LR(1) Parser.

Die folgende Grafik ( Siehe Arbeitsweise der Compiler. ) stellt das Zusammenspiel der bisher erwähnten Komponenten dar.

Arbeitsweise der Compiler
Dokument-Compiler

Der Dokument-Compiler hat die Aufgabe, Html-Dokumente nach Tags zu durchsuchen und die Tags mit ihren Parametern an den Erweiterung-Compiler weiterzureichen. Im Html-Dokument enthaltene Kommentare werden gelöscht. Alles was kein Tag ist, bleibt unverändert. Die Tags werden durch das Ergebnis des Erweiterung-Compilers ersetzt.

Die nachfolgende Siehe Arbeitsweise des Dokument-Compilers. zeigt exemplarisch die Arbeitsweise des Document-Compilers anhand eines Beispieldokumentes.

Arbeitsweise des Dokument-Compilers

Nachdem ein Dokument an die Auswertung des Compilers übergeben wurde, wird der Text an den Scanner weitergereicht. Der Scanner zerlegt den Text in Tokens und übergibt diese an den Parser, welcher dann die Aufgabe hat, Tags und ihre Parameter zu erkennen und an den Erweiterung Compiler weiterzuleiten.

Während der Initialisierung wird auch der Makrovariablen-Speicher aufbereitet. Es bleiben also nur Projekt-Variablen erhalten. Zuvor definierte Dokument-, Tag- oder Makrovariablen verlassen an dieser Stelle ihren Geltungsbereich.

Erweiterung-Compiler

Der Makro-Compiler hat die Aufgabe, zu überprüfen, ob für einen Tag eine neue Definition vorliegt, und diese gegebenenfalls aufzubereiten. Existiert keine neue Definition, so wird der Tag unverändert zurückgegeben. In der neuen Definition existierende Beschreibungen werden gelöscht. Variablen werden durch ihren Wert ersetzt. Makros werden an den Makro-Compiler weitergereicht und durch dessen Ergebnis ersetzt.

Die nachfolgende Siehe Arbeitsweise des Erweiterung-Compilers. zeigt exemplarisch den Übersetzungsvorgang eines Erweiterungs-Dokumentes.

Arbeitsweise des Erweiterung-Compilers

Nachdem ein Tag an die Auswertung des Compilers übergeben wurde, überprüft sie ob zu diesem Tag eine neue Definition existiert. Die neue Definition wird an den Scanner weitergereicht. Der Scanner zerlegt den Text in Tokens und reicht diese an den Parser weiter, welcher dann die Aufgabe hat, Variablen und Makros zu erkennen. Variablen werden durch ihren Wert ersetzt, Makros werden an den Makro-Compiler weitergeben.

Während der Initialisierung wird auch der Makrovariablen Speicher aufbereitet. Es bleiben also nur Projekt- und Dokument-Variablen erhalten. Zuvor definierte Tag- oder Makro-Variablen verlassen an dieser Stelle ihren Geltungsbereich.

Makro-Compiler

Der Makro-Compiler hat die Aufgabe, Makros auszuführen. Es ist also eigentlich kein Compiler, sondern eher ein Interpreter. Das Makro wird ausgeführt und die Print-Ausgaben werden zurückgegeben.

Die folgende Siehe Arbeitsweise des Makro-Compilers. zeigt die Arbeitsweise des Makro-Compilers anhand eines Beispielmakros.

Arbeitsweise des Makro-Compilers

Nachdem ein Makro an die Auswertung des Compilers übergeben wurde, wird der Text an den Scanner weitergereicht. Der Scanner zerlegt den Text in Tokens und reicht diese an den Parser weiter, welcher dann die Syntax der Makros zu erkennen und die Kommandos auszuführen hat.

Die in den Makros vorkommenden Funktionen und Operationen sind in der Auswerten-Klasse definiert.

Während der Initialisierung wird auch der Makrovariablen-Speicher aufbereitet. Zuvor definierte Makrovariablen verlassen an dieser Stelle ihren Geltungsbereich.

Der Interpreter

Die Realisierung des Makro-Interpreters weicht ein wenig von der Realisierung eines Compilers ab. Der Grund ist, das ein Compiler für jede Anweisung, die im Quelltext vorkommt, ein Ergebnis erzeugt, also vereinfacht gesagt Quellcode gegen Zielcode austauscht. Eine solche Arbeitsweise ist für einen Interpreter nicht geeignet.

Wenn in einem Interpreter eine bedingte Ausführung von Code gefordert wird, muss die Bedingung sofort geprüft und nicht übersetzt werden. Der folgende Code darf nur bei einer wahren Bedingung übersetzt werden. Ein Compiler würde die Bedingung übersetzen und den dazugehörigen Code auch, ohne die Bedingung zu prüfen.

Das gleiche Problem tritt ein, wenn eine Schleife gefordert ist. Ein Code, der in einer Schleife steht, muss mehrmals übersetzt werden, solange die Bedingung der Schleife erfüllt ist. Der Schleifenkonstrukt jedoch, muss lediglich ausgeführt werden.

Ein weiteres Problem ist, dass bedingte Ausführungen oder Schleifen ineinander verschachtelt sein können. Innerhalb einer Schleife können weitere Schleifen auftreten.

Realisiert habe ich dies, indem der Interpreter einen Code nur dann ausführt, wenn er nicht innerhalb einer bedingten Ausführung steht. Bedingter Code wird erst dann ausgeführt, wenn feststeht, dass er ausgeführt werden soll. Die Ausführung des Codes geschieht dann, indem der bedingte Code erneut dem Makro-Compiler übergeben wird. Bei Schleifen wird der bedingte Code mehrmals übersetzt bzw. ausgeführt.

Grafisches Interface

Wie bereits erwähnt wird es zwei ®Gui geben. Eines, das in Java realisiert ist, damit der Generator als eigenständiges Programm laufen kann, das andere als Website, um eine einfache Bedienung mit Vorschau anbieten zu können.

Da das Gui Funktionen zur Auswahl der Arbeitspfade zur Verfügung stellt, macht es Sinn, auch das Speichern der vorgenommenen Einstellungen an dieser Stelle zu realisieren.

Damit der Übersetzer nicht das Gui blockiert, während er arbeitet, wird er als Thread gestartet. Um trotzdem Rückmeldung über den Fortgang der Übersetzung zu erhalten, gehen beide Varianten unterschiedliche Wege. Gemeinsam ist beiden Varianten, dass alle Statusmeldungen an eine Funktion übergeben werden, die jedes Gui zur Verfügung stellen muss.

Java-Umgebung

Sobald der Compiler gestartet wurde, meldet er durch Aufruf einer Funktion des Gui, dass er nun arbeitet. Nach Abschluss des Übersetzungsvorganges meldet er wiederum, dass er fertig ist.

Die Statusmeldungen werden von der Statusfunktion direkt in das Statusfenster weitergelenkt.

Html-Umgebung

Bei dieser Variante gestaltet sich das Ganze ein wenig umständlicher. Da das Javaprogramm nicht direkt auf die Webseite und ihre Elemente zugreifen kann (ich konnte keine Möglichkeit in Erfahrung bringen), fragt ein JavaScript-Programm in regelmäßigen Abständen nach, ob neue Informationen vorliegen. Liegen neue Informationen vor so werden sie dargestellt.

Hierfür stellt der Generator drei Funktionen zur Verfügung. Die erste speichert den aktuellen Zustand des Übersetzers, also ob er noch arbeitet oder fertig ist. Die zweite speichert alle eingehenden Statusmeldungen, bis sie abgerufen wurden. Die Dritte speichert den Pfad zu der Seite, die gerade übersetzt wurde, bzw. das Übersetzungsergebnis dieser Seite.

Die Information über die aktuell übersetzte Seite dient der Vorschau der Seiten. Sobald diese Information bereitsteht, wird die Seite angezeigt.

Implementierung

In den vorherigen Abschnitten habe ich bereits die Voraussetzungen und die Struktur des Generators beschrieben. Im letzten Abschnitt bin ich dann auf die Besonderheiten der Umsetzung eingegangen. In diesem Abschnitt soll es um die programmiertechnische Umsetzung gehen.

Entwicklungsumgebung

Den Generator habe ich mit dem ® Java 2 SDK V1.2.2Win32 erstellt. Der Übersetzer benutzt lediglich den Befehlssatz der ® JVM 1.1 um kompatibel zu Browsern zu bleiben. Als Hilfsmittel zur Erstellung des Java-Gui habe ich den Borland J-Builder V3.5 verwendet. Das Webinterface habe ich für den ® MSIE 5.0 erstellt.

Eine Einschränkung für das Webinterface auf den MSIE ist sinnvoll, da sich bei diesem die Sicherheitseinschränkungen in Bezug auf Java einfach abstellen lassen. Die Einschränkung auf die Version 5.x ist erforderlich, da die Einstellungen bezüglich Java-Applets von älteren Versionen ignoriert werden oder überhaupt nicht vorgenommen werden können. Es ist unproblematisch, das Webinterface nur für den MSIE zu erstellen, da dieser auf allen Rechnern installiert ist, an denen der Generator benötigt wird.

Grafisches Interface

Html Gui

Die einzigen nennenswerten Besonderheiten dieses Teils sind die Speicherung der Einstellungen, die Kommunikation mit dem Übersetzer und die Darstellung der Vorschau. Der Rest ist einfachste Html Programmierung, deren Arbeitsweise direkt aus dem mitgelieferten Sourcecode ersichtlich ist.

Das Speichern der vorgenommenen Einstellungen wie Pfade und Debuglevel habe ich über Cookies realisiert. Dadurch ist gewährleistet, dass jeder Benutzer seine eigenen Einstellungen vornehmen kann. Durch Angabe eines Verfallsdatums für das Cookie habe ich gleichzeitig sichergestellt, das ein Benutzer, der den Generator lange Zeit nicht benutzt hat, die Standard-Einstellungen vorfindet.

Die Kommunikation mit dem Übersetzer übernimmt ein JavaScript-Programm. Mit dem Start eines Übersetzungsvorganges beginnt das JavaScript-Programm in Abständen von 100ms zu überprüfen, ob neue Statusmeldungen anstehen und ob bereits eine Seite fertig übersetzt wurde.

Die Statusmeldungen werden dann in einem Textfeld ausgegeben.

Fertig übersetzte Seiten werden in einem eigenem Fenster angezeigt. Hierfür gibt es zwei verschiedene Varianten.

In der einfachsten liefert der Übersetzer die Adresse, wohin die fertig übersetzte Seite gespeichert wurde. Diese Adresse wird dann angezeigt:

 
fenster.location.href = result;

Als zweites gibt es noch die Möglichkeit, dass lediglich eine Vorschau stattfinden soll. In diesem Fall wird die übersetzte Seite nicht abgespeichert. In diesem Fall liefert der Übersetzer den Quelltext der fertig übersetzten Seite zurück. Diese wird dann direkt in das Fenster geschrieben:

 
fenster.document.open();
 
fenster.document.write( result );
 
fenster.document.close();

Das Resultat der Implementierung hat dann das in Siehe Html Gui - Schnapshot. dargestellte Aussehen:

Html Gui - Schnapshot

Das folgende Klassendiagramm ( Siehe Generator Klasse und seine Module. ) zeigt die Schnittstellenklasse und die Module des Generators.

Generator Klasse und seine Module
Java Gui

Dieses Gui hat nur wenig Besonderheiten.

Erwähnenswert ist, dass Benutzereinstellungen in einer lokalen Datei gespeichert werden, und zwar in Textform in dem Format:

 
Parameter = Wert 

So lassen sich diese Einstellungen einfach mit einem Texteditor überprüfen und bearbeiten.

Die Kommunikation mit dem Übersetzer ist einfach gehalten. Das Gui ruft zum Start den Übersetzer auf, welcher als eigenständiger Thread läuft. Die Rückmeldungen des Übersetzers geschehen über in einem Interface definierte Funktionen.

Eine Möglichkeit der automatischen Vorschau während des Übersetzungsvorganges oder gar eine Vorschau ohne vorheriges Speichern der übersetzten Seiten ist nicht gegeben.

Das Folgende Klassendiagramm ( Siehe Java Gui - Schnapshot. ) zeigt das Gui Paket.

Gui Klasse

Die Funktionsweise des Gui ist leicht aus dem mitgelieferten Sourcecode ersichtlich.

Das Resultat der Implementierung hat dann das in Siehe Java Gui - Schnapshot. dargestellte Aussehen:

Java Gui - Schnapshot

Generator

Über die Generator-Klasse wird der Generator gestartet. Die Generator-Klasse übernimmt dabei die Aufgabe, den Generator zu initialisieren und zu entscheiden, ob eventuell mitgelieferte Parameter ausreichen, um direkt den Übersetzer zu starten, oder ob das Java-Gui gestartet werden muss. Im Fall eines Applets übernimmt diese Klasse auch die Kommunikation mit dem Webinterface.

Übersetzer

Obwohl der Übersetzer den umfassensten Teil des Generators ausmacht, beschreibe ich in diesem Abschnitt nur auf einige wenige Teile noch detailierter. Die meisten Teile sind schon anhand der bisherigen Beschreibungen und des mitgelieferten Sourcecode leicht zu verstehen, weshalb ich auf diese nicht weiter eingehe.

Vorab beschreibe ich jedoch noch kurz die Aufgabe einiger Klassen:

Makro-Parser

Auf die Arbeitsweise des Makro Parsers möchte ich noch einmal gesondert eingehen, weil dieser eine besondere Arbeitsweise aufweist. Er muss einen Code, der nur bedingt ausgeführt wird oder innerhalb von Schleifen steht, besonders behandeln. Dieser Code darf nur übersetzt werden, wenn die Bedingung wahr ist. Andernfalls muss er ignoriert werden. Um dies zu realisieren, zählt der Parser eine Variable hoch, sobald er erkennt, dass ein bedingt ausgeführter Code folgen wird. Jedes Mal, wenn der Parser auf ein IF, ein WHILE oder ein FOR trifft, zählt er diese Variable hoch. Mit dem Verlassen des bedingten Bereiches wird die Variable wieder heruntergezählt.

Solange diese Variable nicht den Wert Null hat wird kein Code mehr ausgeführt. Statt dessen wird der Code selbst zurückgeliefert. Die Bedingung muss nun entscheiden, ob der Code übergangen werden soll (Bedingung ist nicht erfüllt) oder ob der Code erneut einem Makro-Compiler übergeben wird (Bedingung ist erfüllt).

Hierzu ein Beispiel, bei dem der folgende Code dem Makro-Compiler übergeben wird.

 
if( true )
 
  Print( "Bedingung ist wahr" );

Hierauf reagiert der Parser wie folgt:

[if] --> bedingung erkannt --> bedingt++;

[( true )] --> Ausdruck 1 merken

[Print( Bedingung ist wahr" )] --> Ausdruck 2 nicht interpretieren sondern als Ergebnis zurückgeben, da bedingt>0

[;] --> Bedingung zu Ende --> bedingt--; und ersten Ausdruck auswerten. Da dieser wahr ist, wird der zweite Ausdruck an den Makro Compiler übergeben und der gesamte Ausdruck durch dieses Ergebnis ersetzt.

Die gleiche Arbeitsweise funktioniert auch bei Schleifen, nur dass in einem solchen Fall der zweite Ausdruck so oft ausgeführt wird, bis der erste Ausdruck nicht mehr wahr ist.

Dass die Variable bedingt" als Zähler realisiert ist, ist daher wichtig, da sich innerhalb der Bedingung noch weitere Bedingungen befinden können, die beim Verlassen der Bedingung ebenfalls die Variable zurücksetzen.

Abgesehen von dieser besonderen Behandlung arbeitet der Interpreter wie ein ganz normaler Compiler. Alles weitere ist daher auch leicht anhand des mitgelieferten Sourcecodes zu verstehen.

Variablen

Wie ich bereits beschrieben habe, gibt es Variablen mit unterschiedlichen Geltungsbereichen. Diese Variablen unterscheiden sich anhand der Anzahl der vorangestellten Dollarzeichen '$'. Diese Art der Definition birgt einen großen Vorteil. Ist beispielsweise die Ausführung eines Makros beendet, werden alle Variablen gelöscht, die kein führendes Dollar haben. Alle übrigen Variablen werden von der aufrufenden Funktion übernommen. Wird nun die Definition einer Erweiterung verlassen, werden alle Variablen, die nicht mit mindestens zwei Dollarzeichen beginnen, verworfen und nur der Rest wird übernommen. Das gleiche gilt sinngemäß, wenn die Übersetzung eines gesamten Dokumentes abgeschlossen ist.

Syntax der Compiler

Dokumente

Die Syntax der Dokumente, also der Dateien, die Erweiterungen enthalten, weicht nicht von der Syntax normaler Html-Dokumente ab.

Erweiterungen werden in Form von Tags in die Dokumente integriert. Jede Erweiterung ist also ein eigenes Tag, welches genauso wie jedes andere Tag Parameter haben kann.

Soll ein Tag nicht durch seine neue Definition (Erweiterung) ersetzt werden, so genügt es, dem Tag vor der schließenden spitzen Klammer ein Ausrufezeichen hinzuzufügen.

Welche zusätzlichen Tags (Erweiterungen) verwendet werden können, hängt davon ab, welche definiert wurden und zum Zeitpunkt der Übersetzung zur Verfügung stehen, sich also im Erweiterungspfad befinden.

Erweiterung

Erweiterungen bestehen aus zwei Arten von Code.

Zum einen bestehen sie aus Html-Code die sich nur dadurch vom normalen Html unterscheiden, dass sie auch Variablen enthalten können, die während der Übersetzung durch ihren derzeitigen Wert ersetzt werden. Variablen sind durch ein führendes Dollarzeichen und einem darauf folgenden alphanumerischen Bezeichner gekennzeichnet. Sie können an jeder beliebigen Position im Html Code vorkommen. Das Dollarzeichen selbst wird durch einen vorangestellten Backslash '\' dargestellt.

Die zweite Art von Codes sind Makros. Sie werden durch die Schlüsselworte <makro> und </makro> eingeschlossen. Dieser Teil des Codes wird durch die Ausgaben des Makros ersetzt.

Makro

Die Syntax ist dieselbe wie in JavaScript, jedoch ohne Berücksichtigung der groß- und Kleinschreibung.

Variablen

Variablen haben verschiedene Geltungsbereiche, die wie folgt gekennzeichnet sind:

  • name - Variable innerhalb des Makros
  • $name - Variable innerhalb der Erweiterung
  • $$name - Variable innerhalb des Dokumentes
  • $$$name - Variable innerhalb des Projektes

Wobei name " der Name der Variable ist.

Mit Projekt" ist der gesamte Übersetzungsvorgang gemeint. Ein Projekt kann auch mehrere Dokumente beinhalten, wenn ein Dokument die Übersetzung eines weiteren Dokumentes veranlasst.

Innerhalb von Makros definierte Variablen verlassen ihren Geltungsbereich erst mit dem Beenden des Makros.

Funktionen

Es sind bisher folgende Funktionen realisiert:

  • Print ( wert ) - fügt wert" in das Html Dokument ein
  • System ( wert ) - gibt wert" als Statusanzeige aus
  • Message ( wert ) - gibt wert" als Message (mit Zeilenangabe) aus
  • Set ( wert1, wert2 ) - weist der Variable die durch wert1" beschrieben wird, den wert2" zu
  • GetVariablenliste ( filter ) - listet alle Variablen auf, die mit filter" beginnen (var1=wert1" var2=wert2" ...)
  • Scan ( filename ) - übersetzt die durch filename" bezeichnete Datei
  • ScanText ( text ) - übersetzt den in text" übergebenen Inhalt als Dokument
  • Exec ( text ) - übersetzt den in text" übergebenen Inhalt als Makro
  • FileOut ( filename, text ) - Speichert text" in der durch filename" beschriebenen Datei
  • FileAdd ( filename, text ) - Hängt text" an der durch filename" beschriebenen Datei an
  • FileIn ( filename ) - Lädt den Inhalt der durch filename" beschriebenen Datei
  • SubString ( begin, text, end ) - Gibt einen SubString von text" zurück, der nach dem erstem auftreten von begin" anfängt und vor dem letztem Auftreten von end" endet
  • SubString ( n1, text, n2 ) - Gibt einen SubString von text" zurück, der an der n1'sten Position anfängt und an der n2'sten Position von hinten endet
  • SubStr ( n1, text, n2 ) - Gibt einen SubString von text" zurück, der an der n1'sten Position anfängt und n2 Stellen lang ist
  • ReplaceString ( text, alt, neu ) - Tauscht jedes auftreten des Strings alt" in text" gegen den String neu" aus
  • ReplaceChar ( text, alt, neu ) - Tauscht jedes auftreten des Zeichens alt" in text" gegen das Zeichen neu" aus

Konstrukte

Es gibt folgende Konstrukte:

  • IF ( bedingung ) anweisung
  • IF ( bedingung ) anweisung1 ELSE anweisung2
  • WHILE ( bedingung ) anweisung
  • FOR ( aktion ; bedingung ; aktion ) anweisung

bedingung - Ein Vergleich bzw. true " oder false "

Vorbelegte Variablen

Volgende Variablen sind vorbelegt:

  • true - Der Wert true"
  • false - Der Wert false"
  • $filename - Name der Makrodatei
  • $$filename - Name des Dokumentes
  • $$$quellpfad - Der beim Aufruf übergebene Quellpfad
  • $$$zielpfad - Der beim Aufruf übergebene Zielpfad

Vorbelegte Dateien

Folgende Dateien sind vorbelegt:

  • erweiterung/_init.erw - Diese Datei wird zu Beginn des Übersetzungsvorganges eines Projektes ausgeführt
  • erweiterung/_compile.erw - Diese Datei wird zum Übersetzen jeder Html-Datei aufgerufen. Ihr wird in dem Parameter filename die zu übersetzende Datei übergeben

Test

Der Generator wurde in drei Phasen getestet.

In der ersten Phase habe ich den Generator mit fehlerhaften Dateien versorgt. Dadurch lässt sich feststellen wie, fehlertolerant der Generator ist, und ob die entstehenden Fehlermeldungen ausreichend präzise sind.

In der zweiten Phase habe ich den Generator mit selbst erstellten Dokumenten gefüttert. Diese Dokumente enthalten zusammen alle bisher erstellten Erweiterungen, wodurch sichergestellt ist, dass diese die gewünschten Ergebnisse bringen. Es handelt sich bei den Dokumenten um die Beispielseiten.

In der dritten Phase musste der Generator Dokumente übersetzen, die im Rahmen des Weiterbildungs projektes erstellt wurden. Diese Dokumente wurden nach einer grundlegenden Einweisung mit Hilfe der beiliegenden Dokumentation erstellt. Durch diese Dokumente ist sichergestellt, dass der Generator auch alltags tauglich ist und nicht nur mit optimal erstellten Dokumenten arbeitet. Hinzu kommt, dass diese Dokumente typische Anwenderfehler enthalten, wodurch auch die Fehlertoleranz und die Qualität der Fehlermeldungen getestet werden kann.

Um den Generator nicht nur anhand von gelieferten Ergebnissen prüfen zu können, verfügt er über die Möglichkeit, in einen Debug-Modus versetzt zu werden. In diesem Zustand liefert er abhängig vom Debuglevel unterschiedlich ausführliche Statusinformationen, anhand derer die korrekte Arbeitsweise leicht überprüft werden kann.

Performancetest

Zusätzlich zu den Funktionstests habe ich den Generator noch einem Performance-Test unterzogen. Hierbei habe ich die beiden Versionen (Applet und Applikation) verglichen. Zu diesem Test gehört eine Untersuchung des benötigten Speichers sowie eine Überprüfung der Arbeitsgeschwindigkeit. Zum testen habe ich den Generator die Lerneinheit Brandels Barkunde" übersetzen lassen. Als Testsystem diente mir ein PentiumIII 600 PC mit 128MB Ram unter Win NT4.

Testergebnis

Die Tests ergaben, dass der Generator entsprechend der Aufgabenstellung arbeitet.

Alle während der Tests noch auftretenden Fehler hatten nur geringe Ausmaße und konnten behoben werden. Zusätzlich zur Fehlerbehebung wurde der Generator in Bezug auf die Benutzerfreundlichkeit noch in einigen Feinheiten optimiert.

Dazu gehört z.B. die Ausgabe einer Warnung, wenn der Pfad der Quelldatei nicht der übliche ist.

Es hat sich gezeigt, dass die Fehlermeldungen in einigen Situationen noch präziser sein könnten. In welchen Situationen dies jedoch im einzelnen vorkommt, wird sich erst im Laufe der Benutzung herausstellen und kann daher auch jetzt noch nicht optimiert werden.

Der Performance-Test hat folgende Maximalwerte ergeben:

Applet

Applikation

Speicherbedarf Programm

13 MB

11 MB

Speicherbedarf Übersetzung

68 MB

46 MB

Zeit Übersetzung

9:20 Min.

2:18 Min.

Die folgenden Diagramme zeigen noch einmal den Speicherbedarf während der Übersetzung. Jeder Teilstrich in x-Richtung sind 6 Sekunden.

Speicherbedarf Applet
Speicherbedarf Applikation

Es ist leicht zu erkennen, dass die Applikation dem Applet in Bezug auf die Performance deutlich überlegen ist. Die Applikation benötigt nur 67% des Speichers und sogar nur 25% der Zeit.

Es war auffällig, dass der Generator während der Übersetzung größerer Projekte im Verlauf immer langsamer wird, also für die Übersetzung einzelner Seiten immer länger braucht. Um dieses Verhalten näher zu untersuchen, habe ich eine Testseite rekursiv übersetzt. Die folgenden Diagramme ( Siehe Rekursiver Aufruf beim Applet. , Siehe Rekursiver Aufruf bei der Applikation. ) zeigen das Verhalten des Generators bei dieser Übersetzung. Anhand dieses Versuches lässt sich ebenfalls das Verhalten bei speicheraufwendigen Übersetzungen gut untersuchen.

Rekursiver Aufruf beim Applet
Rekursiver Aufruf bei der Applikation

Auffällig ist an diesen Diagrammen, dass lediglich das Applet, mit zunehmender Rekursionstiefe, langsamer wird. Die Applikation zeigt ein durchweg lineares Verhalten. Der Speicherbedarf des Applets ist auch erheblich höher als der Bedarf der Applikation. Es werden jedoch nicht mehr als 80 MByte belegt, jedenfalls scheinbar. Zwar steigt nun nicht mehr der verwendete Speicher des Applets, jedoch der belegte Speicher von Windows steigt weiter. Bei der Applikation ist schon früher Schluss. Diese stoppt einfach die Ausführung, sobald der verwendete Speicher 64 MByte erreicht hat. Dieser Effekt ist jedoch im regulären Betrieb nicht relevant, da selbst große Projekte keine solch starke Verschachtelung aufweisen, wie in diesem Versuch und damit auch nicht einen solch großen Speicherbedarf an den Tag legen. Der Grund für diesen Effekt ist vermutlich in dem verwendetem JRE zu suchen.


1. C. Scott Ananian, 1998

Zu Hauptfenster wechseln Zu Vorherigem wechseln Zu Nächstem wechseln