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.
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