Home Up Intro Contents Chapter 1 2 3 4 5 6 7 8 9 10 Design Assert Timing EBNF Report Pas Last Changed: Nov. 19, 1997
This is a conversion from Oberon text to HTML. The converter software is still under development, and some features or information may be missing in this converted version. HTML hypertext facilities are not yet active in this document. To exploit the interactive facilities, use Oberon System 3 and the source of this text, available as ASCII-coded Oberon System 3 documents. A previous version is also available for Oberon V4. To access this and other additional material use ftp.
For the convenience of our students, most of this information and the related material is in German. Sorry if this is not one of your languages.

Einführung in die Programmiersprache Oberon.

G. Sawitzki <gs@statlab.uni-heidelberg.de>



11: Programmentwicklung

Mit Oberon wird das Programm ersetzt durch ein Zusammenspiel von Komponenten. Anstelle eines klassischen Programm-Entwurfs tritt die Auswahl und angepasste Weiterentwicklung von Komponenten und ihre effektive Kombination. Erweiterbarkeit und Wiederverwendbarkeit sind keine zufälligen Nebeneffekte, sondern müssen beim Entwurf berücksichtigt werden. Dies erfordert angepasste Methoden des Programm-Entwurfs und der Programm-Entwicklung.

Methoden des Programmentwurfs sind nur Hilfestellungen, die kreative Arbeit unterstützen können. Sie können die Kreativität aber nicht ersetzen. Einige Leitsätze sollten auf jeden Fall beachtet werden.

- Nicht das Rad neu erfinden! Der Schatz vorhandener Module, bereits gemachter Entwicklungen und nutzbarer Implementierungen ist riesig. In Oberon kann frei auf Dienste vorhandener Module zugegriffen werden. Davon sollte Gebrauch gemacht werden.

- Wiederverwendbarkeit einplanen! Wiederverwendbarkeit von Moduln setzt ihre flexible Brauchbarkeit voraus. Dies sollte beim Entwurf von Moduln berücksichtigt werden. Eine allgemein verwendbare Lösung ist einer Sonderlösung für einen Spezialfall vorzuziehen. Die Sonderlösung kann etwa aus Effktivitätsgründen sinnvoll sein. Dies ist aber von Fall zu Fall zu rechtfertigen. Eine Kompromiss-Strategie ist es, nach geeigneten allgemeinen Abstraktionen zu fragen und eine Implementierung dann in einen allgemeinen wiederverwendbaren Teil und eine Erweiterung für einen Spezialfall aufzuteilen.

- Funktionalitäten trennen! Bessere Implementierungen für einzelne Funktionalitäten können im Laufe der Zeit verfügbar werden. Die Architektur sollte es erlauben, einzelne Module auszutauschen, ohne das Gesamtsystem generell zu verändern. Unterschiedliche Funktionalitäten sollten nicht vermischt werden. Ein Modul sollte nur eine Funktionalität haben. Auch hier gibt es wieder notwendige Kompromisse. Für die Quellverwaltung kann es hilfreich sein, unterschiedliche Funktionalitäten zu bündeln, auch wenn sie nach Design und Implementierung trennbar wären.

- Testbarkeit einplanen! Jedes Modul sollte nur wohl-definierte "innere Zustände" haben. Input und Output sollten über definierte Schnittstellen testbar sein.

Definitions/Relaxationsstrategie
Wir stellen hier eine spezielle Entwurfsstrategie vor. Diese Strategie arbeitet iterativ und ist verwandt der klassischen Methode der schrittweisen Verfeinerung. Im jeden Schritt machen wir eine Reihe von Detail-Schritten:
- Problem-Definition.
  Dies ist eine mehr oder weniger strenge Spezifikation.
- Relaxation.
  Wir ersetzen das definierte Problem durch ein einfacheres
  verwandtes Problem.
- Defizit-Bestimmung.
  Diese beschreibt den Unterschied zwischen dem relaxierten
  Problem und dem übergeordneten vollen Problem
- Implementierung der relaxierten Lösung.
  Diese Lösung kann ad hoc angegeben werden, oder wir iterieren
  das Vorgehen.
- Behebung des Defizits.
  Dies ist ein neues Detail-Problem, das nun die Lösung der
  Relaxation voraussetzen kann.
Routinemässig untersuchen wir sobald eine neue Datenstruktur eingeführt wird, ob Wiederverwendbarkeit eine allgemeinere Struktur empfiehlt oder ob Trennung der Funktionalität über eine Aufteilung in separate Module nahelegt.

Damit unsere Module nutzbringend wiederverwendbar sind, untersuchen wir bei jedem Schritt, ob eine Verallgemeinerung oder Erweiterung sinnvoll ist. Diesen zusätzlichen Detail-Schritt nennen wir Generalisierung.

Ebenso routinemässig führen wir Bereinigungen durch:
- wir überprüfen die Programmquellen auf überholte Komponenten, insbesondere auf Anteile, die im Programmfluss nicht mehr erreicht werden können.
- wir überprüfen die Programmquellen auf wiederholte Strukuren, d.h. auf funktionell gleiche Komponenten, die wiederholt implementiert sind.
Diese Bereinigungen führen wir durch, wann immer eine definierte Funktion stabil implementiert ist.

Fall-Studie: Report
  Beispiel-Implementierungen für diese Fall-Studie haben mit
  ItO .... beginnende Namen (Introduction to Oberon).
Als Beispiel betrachten wir die Aufgabe, einen "Report-Generator" zu schreiben. Seine Aufgabe ist eseine Abhilfe für ein chronisches Problem zu geben: es soll helfen, Programme und Dokumentation konsistent zu halten. Sind Dokumentation und Programm getrennt, so muss jede Programmänderung an anderer Stelle in der Dokumentation berücksichtigt werden. Um das Problem zu reduzieren, wollen wir die Dokumentation im Programm einbetten. Dazu benutzen wir Kommentare. Ein Werkzeug soll helfen, aus der kommentierten Programmquelle die Dokumentation zu extrahieren.

Das Werkzeug soll auch bei der Programmentwicklung nutzbar sein. Wir müssen identifizieren, welche Fragen noch offen sind und wo noch Detail-Schritte zu machen sind. Die Dokumentations-Information hat also unterschiedliche Rollen. Wir benutzen eine Markierung, um diese Rollen zu kennzeichnen. Für diese Markierung reservieren wir das erste Zeichen des Kommentars.

Kommentare, die mit einem Stern beginnen, sind exportierte Kommentare. Diese sollen in der Dokumentation erscheinen.
  (** ..... *)  exportierter Kommentar
Damit wir das Erscheinungsbild steuern können, sehen wir gleich Varianten vor: wiederholte Sterne bedeuten ein höheres Gewicht. Die entsprechenden Kommentare sollten zunehmend herausgehoben werden - etwa entsprechend den "header levels" in HTML. Damit das Layout auch im Quellformat gestaltet werden kann ignorieren wir schleppende Stern-Markierungen, d.h.
  (**** ..... ****)
    exportierter Kommentar, Gewicht +3

entspricht
  (**** ..... *).
Kommentare, die mit einem = beginnen, sind typischerweise Erklärungen. Sie werden in die Dokumentation exportiert, aber nicht besonders herausgehoben.
  (*= ..... *)  exportierter Kommentar, erklärend.
Insbesondere während der Programmentwiclung benutzen wir Kommentare in weiteren Rollen.
  (*! ..... *)   "To do"-Kommentar.
    Hinweis auf eine notwendige
    Erweiterung.
  (*? ..... *)   Offene Fragen und Diskussionspunkte
  (*: ..... *)
    Synchronisierungsmarke für die
    Quellverwaltung
Beginnt der Kommentar mit einem anderen Zeichen als hier definiert, so soll er für die Dokumentation ignoriert werden.

Als Aufrufkonvention wählen wir
  ItOReport.Do Quellname

Wir beginnen mit einer extremen Relaxation: im ersten Schritt wollen wir nur so weit gehen, dass wir den Eingabeparameter korrekt interpretieren. Eine naheliegende Erweiterung auf dieser Stufe ist es, direkt zugängliche Information über die als Quelle angegebene Datei zu berichten. Wir geben diese Information im Oberon.Log aus.

Eine Implementierung ist in
  Kurs/ItOReport.01.Mod
und der Aufruf
  ItOReport.Do Kurs/ItOReport.01.Mod
ist ein erster Testfall.

Das offensichtliche Defizit ist, dass diese Implementierung noch keine Information über den Inhalt von Kommentaren liefert. Im zweiten Schritt extrahieren wir Kommentare. In der relaxierten Lösung berichten wir unterschiedslos alle Kommentare. Das Defizit ist, dass die unterschiedlichen Rollen noch nicht berücksichtigt werden. Dieses Defizit ist im nächsten Schritt zu beheben.

Für den zweiten Schritt müssen wir Kommentare identifizieren. Die formale Definition ist durch die Syntax von Oberon festgelegt: Kommentare beginnen mit der Zeichenfolge (* und enden mit einem schliessenden *). Kommentare können in Oberon geschachtelt sein, und erst wenn die gesamte Verschachtelung abgebaut ist, ist der Kommentar geschlossen. Die Verschachtelungstiefe könnnen wir mit einem Zähler erfassen; der Zählerstand 0 soll dabei bedeuten, dass wir nicht im Bereich eines Kommentars sind.

Eine Relaxation dieses Problems ist die Suche nach Zeichenketten, die durch (* ... *) begrenzt sind. Für dieses relaxierte Problem ist ein neuer Datentyp sinnvoll: wenn wir die gesamte Programmquelle mit dem vordefinierten Typ Text repräsentieren, so haben wir nun Text-Segmente, charakterisiert durch Anfangsposition und Länge, mit einem zusätzlichen Attribut. Dieses Attribut kann ein Segment als Kommentarkette markieren, oder als etwas anderes - für uns nicht von Interesse und als "Stuff" markiert.

Die Segmentierung einer Programmquelle ist potentiell von allgemeinerer Bedeutung. Deshalb implementieren wir diese Struktur in einem getrennten Modul. Letztendlich führt die Segmentierung zu einer Zerlgegung in Tokens - bei uns bis jetzt nur mit zwei Token-Klassen "Kommentar" und "Quelltext". Wir wählen schon jetzt Bezeichnungen, die dem allgemeineren Rahmen entsprechen.

Eine Implementierung ist in
  Kurs/ItOScan.01.Mod
mit einer Implementierung des entprechenden Kommandos in
  Kurs/ItOReport.02.Mod

Das Defizit dieser Implementierung ist: Kommentarbegrenzer sind nur auf Symbol-Ebene wirksam. Kommentarbegrenzer in Strings müssen ignoriert werden. Dieses Defizit ist noch zu beheben. Dieses Defizit ist ein internes Defizit des Scan-Moduls. Wir können es dort beheben, indem wir Strings als neue Token-Variante einführen.
Eine Implementierung ist in
  Kurs/ItOScan.02.Mod
Weil diese Implementierung nur das Scan-Modul intern verändert, bleiben die Kommandos in ItOReport.02 unverändert gültig.

Mit dieser Stufe haben wir einen "Report-Generator", der Kommentare verlässlich berichtet. Das offene Defizit ist: die unterschiedlichen Rollen der Kommentare müssen noch ausgewertet werden. Bevor wir dieses Defizit beheben, ist es Zeit für einen Bereinigungsschritt. Wir inspizieren den Quellcode, entfernen nicht (mehr) erreichbare Komponenten, und vereinheitlichen wiederholte Implementieren der gleichen Funktionalität.
Bereinigte Fassungen sind in
  Kurs/ItOScan.03.Mod
und
  Kurs/ItOReport.03.Mod

Auf diesen bereinigten Fassungen bauen wir nun auf. Wir haben eine verlässliche Identifizierung von Kommentaren und wollen nun deren unterschiedliche Rolle repräsentieren. Dazu müssen wir die unterschiedlichen Kommentare identifizieren und sie dann in geeingneter Weise darstellen. Wir erweitern zunächst den Datentyp tCommentToken, um die zusätzliche Information zu erfassen. Dies ist wieder eine interne Änderung im Scan-Modul und beeinflusst den Aufruf nicht.
Das erweiterte Scan-Modul ist in
  Kurs/ItOScan.04.Mod
und die Kommandos weiterhin in
  Kurs/ItOReport.03.Mod
Mit dieser Stufe können wir prüfen, ob wir die Kommentar-Typen korrekt identifizieren.

Im nächsten Schritt bereinigen wir das Layout. Kommentare mit unterschiedlichen Rollen sollen klar getrennt erscheinen. Dabei müssen wir verschiedene Aspekte berücksichtigen: für viele Betrachter sind Farb-Unterschiede klare Signale. Doch Varianten von Farbblindheit sind weit verbreitet. Farb-Unterschiede sollten durch ein anderes stilistisches Merkmal ergänzt werden. Bei der Druck-Ausgabe ist Farbdruck noch nicht generell verbreitet, so dass wir auf jeden Fall eine andere Markierung vorsehen müssen. Diese Aspekte erfordern in der Praxis eine komplexe Behandlung von Stil-Attributen.

Für unsere Zwecke beschränken wir uns zunächst auf eine vereinfachte Lösung und benutzen nur eine Farb-Kennzeichnung. Diese ist in
  Kurs/ItOScan.05.Mod
implementiert, mit dem zugehörigen Aufruf in
  Kurs/ItOReport.04.Mod
Beispiel:
  ItOReport.Do Kurs/ItOReport.04.Mod ~
  ItOReport.Do Kurs/ItOScan.05.Mod ~


Projekt-Übung:
Überprüfen und korrigieren Sie das Report-Programm. Es sollte die gestellten Spezifikationen verlässlich erfüllen. Stellen Sie eine Liste der Annahmen an die Quellstruktur und der zu berücksichtigen Kommentartypen auf. Prüfen Sie das Programm,
  a) wenn die Annahmen erfüllt sind
  b) wenn eine der Annahmen nicht erfüllt ist, aber die anderen gelten
  c) mit Beispiel-Programmen (sollte korrekt arbeiten)
  d) mit willkürlichen Beispiel-Texten
    (sollte zumindest nicht zu System-Versagen führen)
Korrigieren Sie das Programm.


Wir arbeiten mit der Modell-Implementierung in Kurs/ItOScan.05.Mod bzw. Kurs/ItOReport.04.Mod weiter und versuchen, die Lösung zu generalisieren: wir wollen den Quelltext selbst berücksichtigen. Dies ist in den Spezifikationen nicht enthalten, aber eine naheliegende Erweiterung von allgemeiner Bedeutung.

Um dieses Teilproblem zu lösen, müssen wir den Quelltext interpretieren. Diese Aufgabe haben wir dem Scan-Modul zugeordnet. Das Scan-Modul ist aber bis jetzt nicht in der Lage, mehr als eine grobe Segmentierung zu leisten.

Diese Funktionalität wird in einer Reihe von Moduln erbracht, z.B. vom Compiler, von Watson und ähnlichen Hilfsprogrammen. Wir können also auf Funktionalitäten dieser Programme zurückgreifen, um dieses Teilproblem zu lösen. Leider sind Compiler und ähnliche Programme noch nicht auf Wiederverwertbarkeit angelegt. Sie erhalten zwar die für uns benötigten Programmkomponenten. Konstanten und andere definierende Bestandteile sind aber nicht exportiert. Hier müssen wir eine Design-Entscheidung fällen. Wir können uns auf die jeweils vorliegende Implementierung beschränken und die jeweiligen Compiler-Komponenten benutzen. Oder wir wollen eine implementierungsunabhängige Version. In diesem Fall müssen wir leider Teile des Compilers neu implementieren.

Wir zielen eine implementierungsunabhängige Version an. Dazu erweitern wir in unserem Scanner den Datentyp tToken um eine Klasse tSymbolToken. Diese entspricht in etwa den Tokens des Compilers. Wir müssen für unsere Zwecke jedoch nur einige Bestandteile wie Prozedurköpfe etc. erkennen. Soweit wir absehen können, brauchen wir z.B. nicht arithmetische Ausdrücke auszuwerten. Insbesondere können wir (noch) alle Schwierigkeiten der Erkennung reller Zahlen vermeiden. Wir erlauben, dass der Typ tSymbolToken auch "Rohtoken" repräsentieren kann, die erst nach einem weiteren Verarbeitungsschritt zu Compiler-Token transformiert werden.

Für uns sind diese Token zunächst nur Text-Segmente, die wir als Eingabeeinheit für weitere Schritte benutzen. Für Oberon-Quellen kann der Start dieser Segmente durch ihren (links stehenden) Kontext und ihr erstes Zeichen erkannt werden. Ausserhalb von Strings und Kommentaren sind die wichtigen Startzeichen:
  A-Z, a-z  Name oder reserviertes Word
  0-9  Zahl
  <,=,>,&,...  Symbol
Wir passen unsere Symbol-Codes der aktuellen Version (S3 R2.2) von Oberon an. Und benutzen eine Tabelle, um den Token-Start zu erkennen. Eine Token-Entschlüsselung ist in
  Kurs/ItOScan.06.Mod
Diese ist im Prinzip gleichwertig zu unserem bisherigen Scanner - mit dem Unterschied, dass dieser Scanner zuätzliche Tokens mit den Symbol-Typen sIdent, sNumbe und sLParen erkennen kann. Da wir diese Token noch nicht auswerten, erhalten wir noch keine zusätzliche Information.

Wir relaxieren das Problem, Prozedurdeklarationen in den Bericht aufzunehmen, zum einfacheren Problem, das Codewort PROCEDURE, gefolgt von einem Namen, zu berichten. Die Routine-Entscheidung ist nun, wo diese Funktionalität implementiert wird. Identifikation von Token ist eine Funktionalität, die auf Scanner-Ebene implementiert werden kann, und eine vereinfachte Variante findet sich bereits in Kurs/ItOScan.06.Mod als Prozedur IsTokenText. Kurs/ItOScan.07.Mod enthält eine Verfeinerung, die im Prinzip geeignet ist, reservierte Worte und Symbole zu erkennen. Wir benutzen diese in Kurs/ItOReport.05.Mod, um wieder eine testbare (und verwendbare) Version unseres Report-Generators zu erhalten. Diese Variante erkennt Prozedur-Deklarationen nicht korrekt. Sie sucht lediglich nach dem Muster "PROCEDURE <name>".


Projekt-Übung:
Schreibe ein "Report"-Programm, das aus einer Programmquelle die Prozedurdeklarationen übernimmt und im Bericht einbettet.
Bearbeitungszeit: etwa eine Woche.


Aufgabe dieses Kapitels ist es, eine Entwurfs-Strategie zu vermitteln, die in früheren Programmiersprachen als "schrittweise Verfeinerung" bekannt ist. Für ein erweiterbares System wie Oberon muss diese Strategie modifiziert werden zu einer Strategie der Entwicklung sukzessiver Prototypen, die wir hier an einem Beispiel vorgestellt haben. Es ist nun eine Übungsaufgabe, bei Bedarf mit dieser Strategie einen funktionellen Report-Generator zu entwickeln - eine müssige Übungsaufgabe, denn bei Bedarf gibt es genug gute Report-Generatoren, auf die man zurückgreifen könnte. Das Prinzip des Vorgehens ist hier wichtig, und wir belassen es für die Übung bei einem halbfertigen Resultat. Wir wollen nur noch ein Detail nachtragen. Die Behandlung der Formatierung ist noch rudimentär, und sie verlangt nach einem neuen Datentyp, der einen Ausgabstil abstrakt repräsentiert. Diese Funktionalität ist wieder von allgemeinerer Bedeutung und sollte durch ein getrenntes Modul repräsentiert werden.

Dieser Detail-Punkt wird hier noch ausgeführt, weil wir auf ein typisches Problem treffen. Die Ausgabe in Oberon ist ikonographisch orientiert: ein Zeichensatz ist eine Bibliothek von Zeichen. Wir möchten jedoch die Zeichen mit Attributen versehen (hervorgehoben, kritisch, zu bearbeiten). Wir stehen vor der Notwendigkeit, zwei unterchiedliche Modelle miteinander zu vereinbaren. Diese Aufgabe ist nicht trivial. In Kurs/ItOStyles.Mod ist der Entwurf einer Abstraktion implementiert, die zwischen einem Attribut-System (wie gewünscht) und einem Bibliothekssystem vermitttelt. Kurs/ItOReport.06.Mod enthält eine geringfügige Modifikation, die zumindest für die Überschrift dieses Attribut-System benutzt.

Um den abschliessenden Stand zu installieren benutzen Sie
  System.Free ItOReport ItOScan ItOStyles~
  Compiler.Compile Kurs/ItOStyles.Mod \s ~
  Compiler.Compile Kurs/ItOScan.07.Mod \s ~
  Compiler.Compile Kurs/ItOReport.06.Mod \s ~
und als Test-Kommandos z.B.
  ItOReport.Do Kurs/ItOScan.07.Mod ~
  ItOReport.Do Kurs/ItOReport.06.Mod ~
  ItOReport.Do Kurs/ItOStyles.Mod ~


Projekt-Übung:
Schreibe ein "Report"-Programm, das eine Markierung mit Farbe, Schrifttyp und Stil unterstützt. Dazu sollte ein abstrakter Datentyp für "Stlyes" eingeführt werden. Als Modell kann Kurs/ItOStyles.Mod herangezogen werden. Garantieren Sie insbesondere, dass der Ausgabestil eingehalten wird, unabhängig vom Eingabestil.
Bearbeitungszeit: etwa einen Monat






Weitere Literatur: Reiser&Wirth, Kapitel 10
Weitere Aufgaben: Reiser&Wirth 10.2, 10.3, 10.4


Einführung in die Programmiersprache Oberon. Kurs/Kap11.Text
gs (c) G. Sawitzki, StatLab Heidelberg
<http://statlab.uni-heidelberg.de/projects/oberon/kurs/>

Home Up Intro Contents Chapter 1 2 3 4 5 6 7 8 9 10 Design Assert Timing EBNF Report Pas