mirror of
https://github.com/forth-ev/VolksForth.git
synced 2024-11-22 05:32:28 +00:00
Reformatted Errorhandler Article
This commit is contained in:
parent
449712dcce
commit
cafe4b5ca6
@ -2,14 +2,9 @@ ERR.ART
|
||||
Korrektur clv03jul89
|
||||
Entwurf clv01mar88
|
||||
|
||||
set margins 1 40
|
||||
(mit c-t in Kommandozeile)
|
||||
|
||||
|
||||
Titel:
|
||||
|
||||
Behandlung von Ausnahmesituationen in
|
||||
Forth83
|
||||
Behandlung von Ausnahmesituationen in Forth83
|
||||
|
||||
Stichworte:
|
||||
|
||||
@ -19,194 +14,114 @@ exception handling
|
||||
volksFORTH83
|
||||
returnstack
|
||||
|
||||
Kurzfassung:
|
||||
Ausgehend von einer Analyse
|
||||
des Bed<65>rfnisses nach
|
||||
programmspezifische Fehlerbehandlung
|
||||
(die insbesondere zum erweiterten
|
||||
Begriff des 'Exception handling' f<>hrt)
|
||||
werden Konzepte aus verschiedenen Programmiersprachen
|
||||
sowie zu Forth83 vorgeschlagene Konzepte
|
||||
auf ihre Brauchbarkeit
|
||||
hin diskutiert. Es folgt eine unter
|
||||
ultraFORTH83 rev 3.8 auf einem C16
|
||||
entwickelte
|
||||
L”sung des Autors, die abh„ngig von
|
||||
der Ausf<73>hrungsebene eine spezielle
|
||||
mittels FAILS..THEN installierte
|
||||
Behandlung der Worte ABORT" und ERROR"
|
||||
gestattet.
|
||||
Kurzfassung: Ausgehend von einer Analyse des Bedürfnisses nach
|
||||
programmspezifische Fehlerbehandlung (die insbesondere zum erweiterten
|
||||
Begriff des 'Exception handling' führt) werden Konzepte aus
|
||||
verschiedenen Programmiersprachen sowie zu Forth83 vorgeschlagene
|
||||
Konzepte auf ihre Brauchbarkeit hin diskutiert. Es folgt eine unter
|
||||
ultraFORTH83 rev 3.8 auf einem C16 entwickelte Lösung des Autors, die
|
||||
abhängig von der Ausführungsebene eine spezielle mittels FAILS..THEN
|
||||
installierte Behandlung der Worte ABORT" und ERROR" gestattet.
|
||||
|
||||
o Die derzeitige Fehlerbehandlung
|
||||
in Forth: ABORT"
|
||||
* Die derzeitige Fehlerbehandlung in Forth: ABORT"
|
||||
|
||||
Im 83er-Standard ist das zentrale Wort
|
||||
zur Fehlerbehandlung ABORT". Es
|
||||
gibt den folgenden String als
|
||||
Fehlernachricht aus, versetzt das System
|
||||
in einen (einigermaáen) definierten
|
||||
Zustand und ruft das Top-Level-Wort QUIT
|
||||
auf, das Eingaben von der Tastatur
|
||||
entgegennimmt und verarbeitet.
|
||||
Jedes laufende Programm wird also ohne
|
||||
R<EFBFBD>cksicht auf Verluste gestoppt und
|
||||
gewissermaáen Forth neu gestartet.
|
||||
Eine „hnliche Wirkung haben die Worte
|
||||
ABORT und QUIT.
|
||||
Im 83er-Standard ist das zentrale Wort zur Fehlerbehandlung ABORT". Es
|
||||
gibt den folgenden String als Fehlernachricht aus, versetzt das System
|
||||
in einen (einigermaßen) definierten Zustand und ruft das
|
||||
Top-Level-Wort QUIT auf, das Eingaben von der Tastatur entgegennimmt
|
||||
und verarbeitet. Jedes laufende Programm wird also ohne Rücksicht auf
|
||||
Verluste gestoppt und gewissermaßen Forth neu gestartet. Eine ähnliche
|
||||
Wirkung haben die Worte ABORT und QUIT.
|
||||
|
||||
Im ultraFORTH83/volksFORTH83 gibt es
|
||||
ein Wort ERROR", das sich von ABORT" nur
|
||||
dadurch unterscheidet, daá der
|
||||
Datenstack nicht gel”scht wird.
|
||||
Desweiteren enth„lt dieses Forth eine
|
||||
User-Variable ERRORHANDLER, die es
|
||||
erm”glicht, ein anderes Verhalten von
|
||||
ABORT" und ERROR" zu installieren.
|
||||
Im ultraFORTH83/volksFORTH83 gibt es ein Wort ERROR", das sich von
|
||||
ABORT" nur dadurch unterscheidet, daß der Datenstack nicht gelöscht
|
||||
wird. Desweiteren enthält dieses Forth eine User-Variable
|
||||
ERRORHANDLER, die es ermöglicht, ein anderes Verhalten von ABORT" und
|
||||
ERROR" zu installieren.
|
||||
|
||||
* Was soll eine Fehlerbehandlung können
|
||||
|
||||
o Was soll eine Fehlerbehandlung
|
||||
k”nnen
|
||||
|
||||
Diese Art der
|
||||
Fehlerbehandlung
|
||||
funktioniert zwar meistens recht gut,
|
||||
wirft aber einige Probleme auf.
|
||||
Im folgenden wird versucht, folgende
|
||||
Diese Art der Fehlerbehandlung funktioniert zwar meistens recht gut,
|
||||
wirft aber einige Probleme auf. Im folgenden wird versucht, folgende
|
||||
Stichworte zu diskutieren:
|
||||
|
||||
- Reservierte Ressourcen
|
||||
schlieáen
|
||||
- Das Level, auf dem die Behandlung
|
||||
erfolgt
|
||||
- Informationen <20>ber den
|
||||
Fehlerzustand erhalten.
|
||||
- šbersichtliche Behandlung
|
||||
selten auftretender Ereignisse
|
||||
- Fehler auch w„hrend der
|
||||
Fehlerbehandlung (ohne
|
||||
Endlosschleifen)
|
||||
- Reservierte Ressourcen schließen
|
||||
- Das Level, auf dem die Behandlung erfolgt
|
||||
- Informationen über den Fehlerzustand erhalten.
|
||||
- Übersichtliche Behandlung selten auftretender Ereignisse
|
||||
- Fehler auch während der Fehlerbehandlung (ohne Endlosschleifen)
|
||||
|
||||
Hierbei flieáen jeweils die Erfahrungen
|
||||
des Autors mit MS-DOS, Pascal,
|
||||
Hierbei fließen jeweils die Erfahrungen des Autors mit MS-DOS, Pascal,
|
||||
Modula-2, Fortran und TLC-Lisp mit ein.
|
||||
|
||||
o Schlieáen von Ressourcen
|
||||
* Schließen von Ressourcen
|
||||
|
||||
Das Wort ABORT" (so es im Quelltext
|
||||
vorliegt) zeigt bereits daá im
|
||||
Fehlerfalle gewisse Systemressourcen
|
||||
wieder freigegeben werden m<>ssen.
|
||||
Zumindest d<>rfte in jedem System der
|
||||
Return-stack entleert werden, oft auch
|
||||
der Datenstack, vielleicht werden
|
||||
sogar gewisse Systemvektoren restauriert
|
||||
(insb. f<>r die Standard-Ein/Ausgabe
|
||||
scheint das geraten).
|
||||
Falls das Programm gewisse
|
||||
weitere Ressourcen
|
||||
reserviert hat, werden sie nicht wieder
|
||||
frei gegeben. Dies k”nnte ein ge”ffnetes
|
||||
File sein, das nicht geschlossen wird;
|
||||
ein Semaphor, das gelockt bleibt; ein
|
||||
men<EFBFBD>artiger Bildschirm, der weiter in
|
||||
allen Farben des Spektrums blinkt; eine
|
||||
hoffnungslos verdrehte Schnittstelle
|
||||
etc.. Am auff„lligsten ist eine z.B. auf
|
||||
den Drucker umgeleitete Standardausgabe,
|
||||
wenn sie von ABORT" nicht restauriert
|
||||
wird.
|
||||
In diesem Fall wird schon die Ausgabe
|
||||
der ABORT"-Meldung (auf den Drucker)
|
||||
fehlschlagen, insb. wenn die gew<65>nschte
|
||||
Fehlermeldung "Drucker ausgeschaltet"
|
||||
heiáen mag. Dieser Effekt wird in jedem
|
||||
intelligenten Forth-System nat<61>rlich
|
||||
abgefangen, unter MS-DOS l„át er
|
||||
sich allerdings noch sehr h<>bsch
|
||||
beobachten. Die gelockten Semaphore
|
||||
machen sich allerdings - in seltenen
|
||||
F„llen - auch unter volksFORTH
|
||||
bemerkbar. V”llig hoffnungslos wird der
|
||||
Fall, wenn eine grӇere
|
||||
Stand-Alone-Anwendung (z.B. ein
|
||||
f<EFBFBD>rchterlich kompliziertes Men<65>programm)
|
||||
grade s„mtliche Systemvektoren
|
||||
erfolgreich verbogen hat und nun durch
|
||||
einem j„mmerlich kleinen Fehler
|
||||
(vielleicht einen offengelassener
|
||||
Diskettenschacht) j„h in die
|
||||
Forth-Hauptschleife geschleudert wird.
|
||||
Das Wort ABORT" (so es im Quelltext vorliegt) zeigt bereits daß im
|
||||
Fehlerfalle gewisse Systemressourcen wieder freigegeben werden müssen.
|
||||
Zumindest dürfte in jedem System der Return-stack entleert werden, oft
|
||||
auch der Datenstack, vielleicht werden sogar gewisse Systemvektoren
|
||||
restauriert (insb. für die Standard-Ein/Ausgabe scheint das geraten).
|
||||
Falls das Programm gewisse weitere Ressourcen reserviert hat, werden
|
||||
sie nicht wieder frei gegeben. Dies könnte ein geöffnetes File sein,
|
||||
das nicht geschlossen wird; ein Semaphor, das gelockt bleibt; ein
|
||||
menüartiger Bildschirm, der weiter in allen Farben des Spektrums
|
||||
blinkt; eine hoffnungslos verdrehte Schnittstelle etc.. Am
|
||||
auffälligsten ist eine z.B. auf den Drucker umgeleitete
|
||||
Standardausgabe, wenn sie von ABORT" nicht restauriert wird.
|
||||
|
||||
o Auf welcher Programmebene soll der
|
||||
Fehler behandelt werden?
|
||||
In diesem Fall wird schon die Ausgabe der ABORT"-Meldung (auf den
|
||||
Drucker) fehlschlagen, insb. wenn die gewünschte Fehlermeldung
|
||||
"Drucker ausgeschaltet" heißen mag. Dieser Effekt wird in jedem
|
||||
intelligenten Forth-System natürlich abgefangen, unter MS-DOS läßt er
|
||||
sich allerdings noch sehr hübsch beobachten. Die gelockten Semaphore
|
||||
machen sich allerdings - in seltenen Fällen - auch unter volksFORTH
|
||||
bemerkbar. Völlig hoffnungslos wird der Fall, wenn eine größere
|
||||
Stand-Alone-Anwendung (z.B. ein fürchterlich kompliziertes
|
||||
Menüprogramm) grade sämtliche Systemvektoren erfolgreich verbogen hat
|
||||
und nun durch einem jämmerlich kleinen Fehler (vielleicht einen
|
||||
offengelassener Diskettenschacht) jäh in die Forth-Hauptschleife
|
||||
geschleudert wird.
|
||||
|
||||
In einem Fall
|
||||
wie letzterem w„re es sogar denkbar,
|
||||
den Fehler noch innerhalb der
|
||||
Systemroutinem (in diesem Fall in der
|
||||
Block-Lese-Routine des Betriebssystem)
|
||||
zu beseitigen
|
||||
(z.B. den Benutzer aufzufordern, doch
|
||||
bitte den Schacht zu schlieáen) und
|
||||
anschlieáend fortzufahren, ohne daá
|
||||
das dar<61>berliegende Programm etwas
|
||||
bemerkt. Derartiges kann sogar MS-DOS.
|
||||
Alle Fehler die
|
||||
in Zusammenhang mit Diskettenlaufwerkern
|
||||
stehen werden noch innerhalb des
|
||||
Betriebssystems mit einer Meldung der
|
||||
* Auf welcher Programmebene soll der Fehler behandelt werden?
|
||||
|
||||
In einem Fall wie letzterem wäre es sogar denkbar, den Fehler noch
|
||||
innerhalb der Systemroutinem (in diesem Fall in der Block-Lese-Routine
|
||||
des Betriebssystem) zu beseitigen (z.B. den Benutzer aufzufordern,
|
||||
doch bitte den Schacht zu schließen) und anschließend fortzufahren,
|
||||
ohne daß das darüberliegende Programm etwas bemerkt. Derartiges kann
|
||||
sogar MS-DOS. Alle Fehler die in Zusammenhang mit Diskettenlaufwerkern
|
||||
stehen werden noch innerhalb des Betriebssystems mit einer Meldung der
|
||||
Form:
|
||||
|
||||
allgemeiner Fehler. Kaffee in Laufwerk A:
|
||||
(A)bbruch, (W)iederholen, (I)gnorieren ?
|
||||
|
||||
beantwortet. Der Benutzer kann sich nun
|
||||
f<EFBFBD>r eine der Alternativen entscheiden.
|
||||
Tippt er 'W', so versucht das System
|
||||
denselben Zugriff nochmal. Dies ist bei
|
||||
einem offen gelassenen Schacht n<>tzlich,
|
||||
gegen Kaffee hilft es nat<61>rlich nicht.
|
||||
'A' terminiert das laufende Programm und
|
||||
springt zur<75>ck ins Betriebssystem (in
|
||||
etwa wie unser QUIT). Dies funktioniert
|
||||
meistens, es sei denn das Betriebssystem
|
||||
m”chte selbst Teile seiner selbst von
|
||||
der Diskette lesen: Wir bekommen die
|
||||
beliebte Endlosschleife, bis wir eine
|
||||
saubere Diskette eingelegt haben.
|
||||
Die Alternative 'I' ist die h<>bscheste.
|
||||
Das System vergiát die Operation und
|
||||
kehrt ins rufende Programm zur<75>ck.
|
||||
Dieses arbeitet brav weiter, bis es
|
||||
sich an irgendwelchenden Zufallsergebnissen
|
||||
den Magen verdirbt. Um zum Beispiel
|
||||
des Men<65>-Programms mit offenen
|
||||
Kassettenschacht noch ein Wort zu
|
||||
verlieren: Beim 'W'iederholen ist
|
||||
nat<EFBFBD>rlich trotz allem der
|
||||
Bildschirmaufbau im Eimer. Bis hierher
|
||||
l„át sich erstmal formulieren, daá
|
||||
die Fehlerbehandlung lieber auf der
|
||||
Ebene des Anwenderprogramms erfolgen
|
||||
sollte.
|
||||
beantwortet. Der Benutzer kann sich nun für eine der Alternativen
|
||||
entscheiden. Tippt er 'W', so versucht das System denselben Zugriff
|
||||
nochmal. Dies ist bei einem offen gelassenen Schacht nützlich, gegen
|
||||
Kaffee hilft es natürlich nicht. 'A' terminiert das laufende Programm
|
||||
und springt zurück ins Betriebssystem (in etwa wie unser QUIT). Dies
|
||||
funktioniert meistens, es sei denn das Betriebssystem möchte selbst
|
||||
Teile seiner selbst von der Diskette lesen: Wir bekommen die beliebte
|
||||
Endlosschleife, bis wir eine saubere Diskette eingelegt haben. Die
|
||||
Alternative 'I' ist die hübscheste. Das System vergißt die Operation
|
||||
und kehrt ins rufende Programm zurück. Dieses arbeitet brav weiter,
|
||||
bis es sich an irgendwelchenden Zufallsergebnissen den Magen verdirbt.
|
||||
Um zum Beispiel des Menü-Programms mit offenen Kassettenschacht noch
|
||||
ein Wort zu verlieren: Beim 'W'iederholen ist natürlich trotz allem
|
||||
der Bildschirmaufbau im Eimer. Bis hierher läßt sich erstmal
|
||||
formulieren, daß die Fehlerbehandlung lieber auf der Ebene des
|
||||
Anwenderprogramms erfolgen sollte.
|
||||
|
||||
o Warum eigentlich nur Fehler mit
|
||||
Methoden der Fehlerbehandlung
|
||||
behandeln?
|
||||
* Warum eigentlich nur Fehler mit Methoden der Fehlerbehandlung behandeln?
|
||||
|
||||
Um diesem h<>bschen Wortspiel Sinn zu
|
||||
geben, mag ein anderes Beispiel
|
||||
herhalten.
|
||||
Ein Programm lese
|
||||
Daten von einem File, verarbeite sie und
|
||||
schreibe das Ergebnis auf ein anderes
|
||||
File.
|
||||
Es muá somit (wenn es strukturiert sein
|
||||
m”chte) vor dem Lesen jedes einzelnen
|
||||
Zeichens das Betriebssystem befragen, ob
|
||||
das File vielleicht schon ersch”pft ist.
|
||||
Das ergibt beispielsweise in Pascal
|
||||
endlose Konstrukte des Strickmusters:
|
||||
Um diesem hübschen Wortspiel Sinn zu geben, mag ein anderes Beispiel
|
||||
herhalten. Ein Programm lese Daten von einem File, verarbeite sie und
|
||||
schreibe das Ergebnis auf ein anderes File. Es muß somit (wenn es
|
||||
strukturiert sein möchte) vor dem Lesen jedes einzelnen Zeichens das
|
||||
Betriebssystem befragen, ob das File vielleicht schon erschöpft ist.
|
||||
Das ergibt beispielsweise in Pascal endlose Konstrukte des
|
||||
Strickmusters:
|
||||
|
||||
WHILE not eof(input) DO
|
||||
WHILE not eoLn(input) DO
|
||||
@ -223,29 +138,20 @@ endlose Konstrukte des Strickmusters:
|
||||
ENDIF;
|
||||
END;
|
||||
|
||||
Man verzeihe es mir, wenn ich die
|
||||
Pascal-Syntax nicht mehr so besonders
|
||||
gut kann. Das Strickmuster sollte
|
||||
eigentlich etwas anderes zeigen: In
|
||||
Pascal erfordert jeder Zugriff eines
|
||||
Programms (so es sich strukturiert nenn
|
||||
will) das Abfragen auf End-Of-File und
|
||||
End-Of-Line. Letzteres ist n”tig, da der
|
||||
Standard leider (?) nicht vorschreibt,
|
||||
wie EOLn am Fileende aussieht. Soll
|
||||
innerhalb der groáen WHILE-Schleife ein
|
||||
weiteres Zeichen gelesen werden, so muá
|
||||
beides erneut gepr<70>ft werden, daá System
|
||||
„rgert sich mit st„ndigem Abgefrage
|
||||
herum, der Programmierer mit der
|
||||
Definition der Routine 'schweinkram',
|
||||
die ihren Namen i.a. zu Recht tr„gt.
|
||||
Man verzeihe es mir, wenn ich die Pascal-Syntax nicht mehr so
|
||||
besonders gut kann. Das Strickmuster sollte eigentlich etwas anderes
|
||||
zeigen: In Pascal erfordert jeder Zugriff eines Programms (so es sich
|
||||
strukturiert nenn will) das Abfragen auf End-Of-File und End-Of-Line.
|
||||
Letzteres ist nötig, da der Standard leider (?) nicht vorschreibt, wie
|
||||
EOLn am Fileende aussieht. Soll innerhalb der großen WHILE-Schleife
|
||||
ein weiteres Zeichen gelesen werden, so muß beides erneut geprüft
|
||||
werden, daß System ärgert sich mit ständigem Abgefrage herum, der
|
||||
Programmierer mit der Definition der Routine 'schweinkram', die ihren
|
||||
Namen i.a. zu Recht trägt.
|
||||
|
||||
Viel einfacher haben es da Sprachen, die
|
||||
keinerlei Anspruch auf strukturiertes
|
||||
Programmieren erheben. Das obige
|
||||
Kuddelmuddel lieáe sich in Fortran etwa
|
||||
so umgehen:
|
||||
Viel einfacher haben es da Sprachen, die keinerlei Anspruch auf
|
||||
strukturiertes Programmieren erheben. Das obige Kuddelmuddel ließe
|
||||
sich in Fortran etwa so umgehen:
|
||||
|
||||
10 READ (input,char,end=100,err=200)
|
||||
IF char.eq..... THEN
|
||||
@ -256,60 +162,36 @@ so umgehen:
|
||||
100 ...<fileEnde behandeln>...
|
||||
200 ...<sonstige Fehler behandeln>...
|
||||
|
||||
Die 'end=' und 'err=' Sequenzen sind
|
||||
verkappte GOTOs.
|
||||
Hier wird (v”llig unstrukturiert) der
|
||||
Programmfluá im Fehlerfall unterbrochen
|
||||
und an den durch 100 und 200
|
||||
gekennzeichneten Stellen fortgesetzt.
|
||||
Ein „hnliches GOTO-Konstrukt bietet auch
|
||||
Pascal an. Jedes gute Lehrbuch bittet
|
||||
aber darum es m”glichst nie zu benutzen.
|
||||
Als einzigen tolerierbaren Zweck wird
|
||||
meist die Fehlerbehandlung angegeben.
|
||||
Ein sehr treffender Beleg daf<61>r, daá
|
||||
grade dem Vater der strukturierten
|
||||
Programmierung, Herrn Wirth, die
|
||||
Fehlerbehandlung Kopfzerbrechen
|
||||
Die 'end=' und 'err=' Sequenzen sind verkappte GOTOs. Hier wird
|
||||
(völlig unstrukturiert) der Programmfluß im Fehlerfall unterbrochen
|
||||
und an den durch 100 und 200 gekennzeichneten Stellen fortgesetzt. Ein
|
||||
ähnliches GOTO-Konstrukt bietet auch Pascal an. Jedes gute Lehrbuch
|
||||
bittet aber darum es möglichst nie zu benutzen. Als einzigen
|
||||
tolerierbaren Zweck wird meist die Fehlerbehandlung angegeben. Ein
|
||||
sehr treffender Beleg dafür, daß grade dem Vater der strukturierten
|
||||
Programmierung, Herrn Wirth, die Fehlerbehandlung Kopfzerbrechen
|
||||
bereitet.
|
||||
|
||||
Was sollte das nun belegen? Es soll
|
||||
zeigen, daá erstens auch v”llig
|
||||
normale Vorg„nge (Ende eines Files)
|
||||
fehlerbehandelt werden wollen. In diesem
|
||||
Fall spricht man von
|
||||
'Ausnahmebehandlung', da dieser
|
||||
Programmierstil nur f<>r selten
|
||||
auftretende F„lle sinnvoll ist. Es soll
|
||||
zweitens zeigen, daá eine Erh”hung der
|
||||
Performance und Wartbarkeit aus
|
||||
sinnvoller Ausnahmebehandlung
|
||||
entspringt. Denn sicher spart das obige
|
||||
Fortran-beispiel einigen Code. Dies
|
||||
spart auch Zeit, da an weniger
|
||||
Stellen auf Fehler gepr<70>ft
|
||||
werden muá. Und ich kann es besser
|
||||
lesen. (Zur Frage der Lesbarkeit steht
|
||||
mir als Nicht-Informatiker kein
|
||||
allgemeines Urteil zu. Die
|
||||
'ich'-Form spart mir hier sicher wieder
|
||||
Was sollte das nun belegen? Es soll zeigen, daß erstens auch völlig
|
||||
normale Vorgänge (Ende eines Files) fehlerbehandelt werden wollen. In
|
||||
diesem Fall spricht man von 'Ausnahmebehandlung', da dieser
|
||||
Programmierstil nur für selten auftretende Fälle sinnvoll ist. Es soll
|
||||
zweitens zeigen, daß eine Erhöhung der Performance und Wartbarkeit aus
|
||||
sinnvoller Ausnahmebehandlung entspringt. Denn sicher spart das obige
|
||||
Fortran-beispiel einigen Code. Dies spart auch Zeit, da an weniger
|
||||
Stellen auf Fehler geprüft werden muß. Und ich kann es besser lesen.
|
||||
(Zur Frage der Lesbarkeit steht mir als Nicht-Informatiker kein
|
||||
allgemeines Urteil zu. Die 'ich'-Form spart mir hier sicher wieder
|
||||
einigen Streit mit Helge und Gerd.)
|
||||
|
||||
o Ist Ausnahmebehandlung
|
||||
auf ganz tiefer Ebene verzichtbar ?
|
||||
* Ist Ausnahmebehandlung auf ganz tiefer Ebene verzichtbar ?
|
||||
|
||||
Manchmal muá aber auch eine
|
||||
Ausnahmebehandlung auf ganz tiefer
|
||||
Ebene erfolgen. Als Beispiel sei hier
|
||||
die Ausgabe von Informationen im
|
||||
Fehlerfall genannt. H„ufig m”chte der
|
||||
Benutzer im Fehlerfall wissen, wo der
|
||||
Fehler aufgetreten ist, wie bestimmte
|
||||
Variablen aussahen, etc.
|
||||
Im volksFORTH gibt es ein Wort
|
||||
UNRAVEL, das die Aufrufhierarchie
|
||||
ausgibt.
|
||||
Dies k”nnte etwa so aussehen:
|
||||
Manchmal muß aber auch eine Ausnahmebehandlung auf ganz tiefer Ebene
|
||||
erfolgen. Als Beispiel sei hier die Ausgabe von Informationen im
|
||||
Fehlerfall genannt. Häufig möchte der Benutzer im Fehlerfall wissen,
|
||||
wo der Fehler aufgetreten ist, wie bestimmte Variablen aussahen, etc.
|
||||
Im volksFORTH gibt es ein Wort UNRAVEL, das die Aufrufhierarchie
|
||||
ausgibt. Dies könnte etwa so aussehen:
|
||||
|
||||
FEHLER divide by Zero AUFGETRETEN.
|
||||
Der Fehler geschah in Wort: 0/
|
||||
@ -318,82 +200,54 @@ FEHLER divide by Zero AUFGETRETEN.
|
||||
aufgerufen von: EDIT
|
||||
aufgerufen von: L
|
||||
|
||||
Derartige POST-MORTEM-DUMPS erm”glichen
|
||||
i.a. ein schnelles Lokalisieren des
|
||||
Fehlers. Sie enthalten des ”fteren nicht
|
||||
nur die Aufrufhierarchie sondern
|
||||
diverse Register- und Variablen-Inhalte
|
||||
zum Zeitpunkt des Fehlers (am besten
|
||||
noch aus allen Unterprogrammen...),
|
||||
sodaá sie manchmal den Benutzer eher in
|
||||
hunderten von Seiten Papier ersticken,
|
||||
als ihm bei der Fehlersuche zu helfen.
|
||||
Aber selbst dagegen sind Kr„uter
|
||||
gewachsen. Logitech's Modula-2-Compiler
|
||||
teilt dem Benutzer auáer einer sehr
|
||||
knappen Fehlerbeschreibung nichts mit
|
||||
und schreibt ersatzweise den kompletten
|
||||
Systemzustand auf Diskette, wo man ihn
|
||||
anschlieáend mit einem
|
||||
Post-Mortem-Debug-Programm umgraben
|
||||
kann. Es frage mich bitte niemand, was
|
||||
passiert, wenn die Diskette voll war.
|
||||
Ruft der Fehler dann einen erneuten
|
||||
Post-Mortem-Dump hervor?
|
||||
Derartige POST-MORTEM-DUMPS ermöglichen i.a. ein schnelles
|
||||
Lokalisieren des Fehlers. Sie enthalten des öfteren nicht nur die
|
||||
Aufrufhierarchie sondern diverse Register- und Variablen-Inhalte zum
|
||||
Zeitpunkt des Fehlers (am besten noch aus allen Unterprogrammen...),
|
||||
sodaß sie manchmal den Benutzer eher in hunderten von Seiten Papier
|
||||
ersticken, als ihm bei der Fehlersuche zu helfen. Aber selbst dagegen
|
||||
sind Kräuter gewachsen. Logitech's Modula-2-Compiler teilt dem
|
||||
Benutzer außer einer sehr knappen Fehlerbeschreibung nichts mit und
|
||||
schreibt ersatzweise den kompletten Systemzustand auf Diskette, wo man
|
||||
ihn anschließend mit einem Post-Mortem-Debug-Programm umgraben kann.
|
||||
Es frage mich bitte niemand, was passiert, wenn die Diskette voll war.
|
||||
Ruft der Fehler dann einen erneuten Post-Mortem-Dump hervor?
|
||||
|
||||
Um wieder zum Faden zur<75>ckzukehren:
|
||||
Zumindest zum Post-Mortem-Debuggen
|
||||
(=Entlausen aus einem toten
|
||||
Programm. šbertragen: Infos
|
||||
<EFBFBD>ber den Fehlerzustand erhalten)
|
||||
ist es n”tig Fehler auf niedrigster
|
||||
Ebene zu behandeln.
|
||||
Um wieder zum Faden zurückzukehren: Zumindest zum Post-Mortem-Debuggen
|
||||
(=Entlausen aus einem toten Programm. Übertragen: Infos über den
|
||||
Fehlerzustand erhalten) ist es nötig Fehler auf niedrigster Ebene zu
|
||||
behandeln.
|
||||
|
||||
o Mein Wunsch zur Fehlerbehandlung:
|
||||
Call-with-current-Continuation
|
||||
(CallCC)
|
||||
* Mein Wunsch zur Fehlerbehandlung: Call-with-current-Continuation (CallCC)
|
||||
|
||||
Die sch”nste, allgemeinste Art der
|
||||
Fehlerbehandlung, die ich kenne, ist das
|
||||
CATCH-THROW-Konstrukt aus TLC-Lisp (von
|
||||
T.Allen).
|
||||
Ich habe mir sagen lassen, daá sie unter
|
||||
Zuhilfenahme des
|
||||
Call-with-current-Continuation-Konzepts
|
||||
implementiert ist. Daher gef„llt mir
|
||||
dieses auch sehr gut. Da ich CallCC
|
||||
leider nicht kenne beschr„nke ich mich
|
||||
jetzt aber wieder auf CATCH-THROW.
|
||||
Es wird in folgender Form benutzt:
|
||||
Die schönste, allgemeinste Art der Fehlerbehandlung, die ich kenne,
|
||||
ist das CATCH-THROW-Konstrukt aus TLC-Lisp (von T.Allen). Ich habe mir
|
||||
sagen lassen, daß sie unter Zuhilfenahme des
|
||||
Call-with-current-Continuation-Konzepts implementiert ist. Daher
|
||||
gefällt mir dieses auch sehr gut. Da ich CallCC leider nicht kenne
|
||||
beschränke ich mich jetzt aber wieder auf CATCH-THROW. Es wird in
|
||||
folgender Form benutzt:
|
||||
|
||||
(CATCH name ((expression)
|
||||
(exceptionhandler)))
|
||||
|
||||
Die Bedeutung ist folgende: Wenn w„hrend
|
||||
der Evaluierung (=Ausf<73>hrung auf
|
||||
LISPisch) von EXPRESSION eine Ausnahme
|
||||
mit dem Namen NAME auftreten sollte, so
|
||||
m”ge bitte sofort EXCEPTIONHANDLER
|
||||
evaluiert werden. Ansonsten ist der
|
||||
obige Ausdruck identisch mit:
|
||||
Die Bedeutung ist folgende: Wenn während der Evaluierung (=Ausführung
|
||||
auf LISPisch) von EXPRESSION eine Ausnahme mit dem Namen NAME
|
||||
auftreten sollte, so möge bitte sofort EXCEPTIONHANDLER evaluiert
|
||||
werden. Ansonsten ist der obige Ausdruck identisch mit:
|
||||
|
||||
(expression)
|
||||
|
||||
Eine Ausnahme tritt dadurch auf, daá
|
||||
eine Funktion (=wort auf
|
||||
LISPisch) innerhalb
|
||||
von expression die Funktion
|
||||
Eine Ausnahme tritt dadurch auf, daß eine Funktion (=wort auf
|
||||
LISPisch) innerhalb von expression die Funktion
|
||||
|
||||
(THROW name)
|
||||
|
||||
aufruft.
|
||||
|
||||
Eigentlich ist es verabscheuungsw<73>rdig,
|
||||
von Lisp aus Files zu lesen, da dies dem
|
||||
funktionalen Programmieren zuwiderl„uft.
|
||||
Um aber trotzdem das obige
|
||||
File-Lese-Beispiel nochmal zu
|
||||
strapazieren:
|
||||
Eigentlich ist es verabscheuungswürdig, von Lisp aus Files zu lesen,
|
||||
da dies dem funktionalen Programmieren zuwiderläuft. Um aber trotzdem
|
||||
das obige File-Lese-Beispiel nochmal zu strapazieren:
|
||||
|
||||
(CATCH end-of-file
|
||||
(CATCH end-of-line
|
||||
@ -410,167 +264,114 @@ strapazieren:
|
||||
( ...end-of-file-handler..)
|
||||
)
|
||||
|
||||
Dies sieht nicht nur wundervoll aus mit
|
||||
den vielen Klammern, sondern hat auch
|
||||
eine Wirkung: wenn READCHAR irgendwann
|
||||
(THROW end-of-line) oder (THROW
|
||||
end-of-file) evaluiert, wird einer
|
||||
unsrer HANDLER aufgerufen. Ob READCHAR
|
||||
das tut und ob es in Lisp <20>berhaupt eine
|
||||
Funktion diesen Namens gibt, entzieht
|
||||
sich leider meiner Kenntnis.
|
||||
Selbstverst„ndlich k”nnen solche handler
|
||||
geschachtelt werden. Um DOSOMETHING
|
||||
k”nnte z.B. ein noch
|
||||
spezielleren handler
|
||||
heruminstalliert werden. Eine Ausf<73>hrung
|
||||
von (THROW name) aktiviert jeweils den
|
||||
n„chst„uáeren (h”hergelegenen) handler.
|
||||
Wenn dieser das n”tige getan hat, kann
|
||||
er beispielsweise erneut (THROW name)
|
||||
evaluieren, um wiederum den ihm
|
||||
n„chst„uáeren handler zu aktivieren. Das
|
||||
Spiel l„át sich weitertreiben, bis
|
||||
schlieálich der aller„uáerste (von der
|
||||
LISP-Interpreter-Schleife) installierte
|
||||
handler aufgerufen wird, der („hnlich
|
||||
unserem ABORT") wieder Eingaben von der
|
||||
Tastatur verarbeitet. Selbstverst„ndlich
|
||||
kann jeder handler in der Schlange auch
|
||||
etwas anderes unternehmen,
|
||||
beispielsweise die gescheiterte Aktion
|
||||
Dies sieht nicht nur wundervoll aus mit den vielen Klammern, sondern
|
||||
hat auch eine Wirkung: wenn READCHAR irgendwann (THROW end-of-line)
|
||||
oder (THROW end-of-file) evaluiert, wird einer unsrer HANDLER
|
||||
aufgerufen. Ob READCHAR das tut und ob es in Lisp überhaupt eine
|
||||
Funktion diesen Namens gibt, entzieht sich leider meiner Kenntnis.
|
||||
Selbstverständlich können solche handler geschachtelt werden. Um
|
||||
DOSOMETHING könnte z.B. ein noch spezielleren handler heruminstalliert
|
||||
werden. Eine Ausführung von (THROW name) aktiviert jeweils den
|
||||
nächstäußeren (höhergelegenen) handler. Wenn dieser das nötige getan
|
||||
hat, kann er beispielsweise erneut (THROW name) evaluieren, um
|
||||
wiederum den ihm nächstäußeren handler zu aktivieren. Das Spiel läßt
|
||||
sich weitertreiben, bis schließlich der alleräußerste (von der
|
||||
LISP-Interpreter-Schleife) installierte handler aufgerufen wird, der
|
||||
(ähnlich unserem ABORT") wieder Eingaben von der Tastatur verarbeitet.
|
||||
Selbstverständlich kann jeder handler in der Schlange auch etwas
|
||||
anderes unternehmen, beispielsweise die gescheiterte Aktion
|
||||
wiederholen.
|
||||
|
||||
o Zur<75>ck zu Forth
|
||||
* Zurück zu Forth
|
||||
|
||||
Aus dem gesagten seien noch einmal die
|
||||
Kernpunkte zusammengefaát:
|
||||
- Fehlerbehandlung soll auf jeder
|
||||
beliebigen Programmebene m”chlich sein
|
||||
Kernpunkte zusammengefaßt:
|
||||
|
||||
- Fehlerbehandlung soll auf jeder beliebigen Programmebene möchlich
|
||||
sein
|
||||
- insbesondere auch auf tiefster Ebene
|
||||
- Die Fehlerbehandlungsroutinen sollen
|
||||
geschachtelt werden k”nnen
|
||||
- Fehlerbehandlar sollen M”glichkeiten
|
||||
erhalten, nach Bedarf die
|
||||
fehlerverursachende Routine erneut zu
|
||||
probieren oder die Ausf<73>hrung dem
|
||||
n„chsth”heren
|
||||
Fehlerbehandler weiterzugeben.
|
||||
- Das ganze soll so einfach zu benutzen
|
||||
sein, daá Routinen f<>r selten
|
||||
auftretende Ereignisse einfach zu
|
||||
formulieren sind.
|
||||
- Diese Fehlerbehandlung soll durch das
|
||||
Standardwort ABORT" aktiviert werden.
|
||||
- Die Fehlerbehandlungsroutinen sollen geschachtelt werden können
|
||||
- Fehlerbehandlar sollen Möglichkeiten erhalten, nach Bedarf die
|
||||
fehlerverursachende Routine erneut zu probieren oder die Ausführung
|
||||
dem nächsthöheren Fehlerbehandler weiterzugeben.
|
||||
- Das ganze soll so einfach zu benutzen sein, daß Routinen für selten
|
||||
auftretende Ereignisse einfach zu formulieren sind.
|
||||
- Diese Fehlerbehandlung soll durch das Standardwort ABORT" aktiviert
|
||||
werden.
|
||||
|
||||
Nun wird's konkret:
|
||||
Wo sollen die Informationen <20>ber die
|
||||
installierten Fehlerbehandlungsroutinen
|
||||
abgelegt werden? Auf dem Returnstack, da
|
||||
sich hier am einfachsten eine der
|
||||
jeweiligen Ausf<73>hrungsebene (Wort)
|
||||
angelehnte Datenstruktur bilden l„át.
|
||||
Nun wird's konkret: Wo sollen die Informationen über die installierten
|
||||
Fehlerbehandlungsroutinen abgelegt werden? Auf dem Returnstack, da
|
||||
sich hier am einfachsten eine der jeweiligen Ausführungsebene (Wort)
|
||||
angelehnte Datenstruktur bilden läßt.
|
||||
|
||||
Wie soll die Syntax aussehen? Es soll
|
||||
einfach sein, daher Kontrollstrukturen.
|
||||
Beispielsweise:
|
||||
Wie soll die Syntax aussehen? Es soll einfach sein, daher
|
||||
Kontrollstrukturen. Beispielsweise:
|
||||
|
||||
: name ..<clause1>..
|
||||
FAILS ...errorhandler... THEN
|
||||
..<clause2>.. ;
|
||||
|
||||
Die Bedeutung: Es wird ein Wort NAME
|
||||
definiert. Bei Ausf<73>hrung f<>hrt NAME
|
||||
erst <clause1> aus, installiert
|
||||
anschlieáend einen ERRORHANDLER, f<>hrt
|
||||
dann <clause2> aus und deinstalliert
|
||||
ERRORHANDLER nach Verlassen des Wortes.
|
||||
Sollte innerhalb von <clause2> ein
|
||||
ABORT" ausgef<65>hrt werden, so wird
|
||||
ERRORHANDLER ausgef<65>hrt.
|
||||
Die Bedeutung: Es wird ein Wort NAME definiert. Bei Ausführung führt
|
||||
NAME erst <clause1> aus, installiert anschließend einen ERRORHANDLER,
|
||||
führt dann <clause2> aus und deinstalliert ERRORHANDLER nach Verlassen
|
||||
des Wortes. Sollte innerhalb von <clause2> ein ABORT" ausgeführt
|
||||
werden, so wird ERRORHANDLER ausgeführt.
|
||||
|
||||
Aktivieren des „uáeren ERRORHANDLERS:
|
||||
Sollte innerhalb von ERRORHANDLER das
|
||||
Wort THROW ausgef<65>hrt werden, so wird
|
||||
die Ausf<73>hrung von ERRORHANDLER beendet
|
||||
und die n„chst„uáere
|
||||
Fehlerbehandlungsroutine aktiviert.
|
||||
Aktivieren des äußeren ERRORHANDLERS: Sollte innerhalb von
|
||||
ERRORHANDLER das Wort THROW ausgeführt werden, so wird die Ausführung
|
||||
von ERRORHANDLER beendet und die nächstäußere Fehlerbehandlungsroutine
|
||||
aktiviert.
|
||||
|
||||
Wiederholen der fehlerverursachenden
|
||||
<clause2>: Hier wird's kritisch. Im
|
||||
Gegensatz zu praktisch allen anderen
|
||||
Sprachen liegen die Parameter auf dem
|
||||
Datenstack. Vor einer Wiederholung
|
||||
m<EFBFBD>ssen Daten- und Returnstack repariert
|
||||
werden. Der Returnstack l„át sich
|
||||
problemlos so manipulieren, daá er bei
|
||||
Aufruf von ERRORHANDLER bereits wieder
|
||||
im gew<65>nschten Zustand ist. Falls ein
|
||||
definierter Zustand des Datenstacks
|
||||
gew<EFBFBD>mscht wird, muá allerdings ein
|
||||
spezielles Konstrukt:
|
||||
Wiederholen der fehlerverursachenden <clause2>: Hier wird's kritisch.
|
||||
Im Gegensatz zu praktisch allen anderen Sprachen liegen die Parameter
|
||||
auf dem Datenstack. Vor einer Wiederholung müssen Daten- und
|
||||
Returnstack repariert werden. Der Returnstack läßt sich problemlos so
|
||||
manipulieren, daß er bei Aufruf von ERRORHANDLER bereits wieder im
|
||||
gewünschten Zustand ist. Falls ein definierter Zustand des Datenstacks
|
||||
gewümscht wird, muß allerdings ein spezielles Konstrukt:
|
||||
|
||||
nn #FAILS ..errorhandler.. RETRY
|
||||
|
||||
benutzt werden. Die NN obersten
|
||||
Stackwerte, sowie der Stackpointer
|
||||
werden gesichert. Falls ERRORHANDLER
|
||||
aufgerufen werden sollte, wird der Stack
|
||||
vorher soweit restauriert. Es gilt
|
||||
nat<EFBFBD>rlich aufzupassen, daá sie bis
|
||||
zur Ausf<73>hrung von RETRY auch dableiben.
|
||||
benutzt werden. Die NN obersten Stackwerte, sowie der Stackpointer
|
||||
werden gesichert. Falls ERRORHANDLER aufgerufen werden sollte, wird
|
||||
der Stack vorher soweit restauriert. Es gilt natürlich aufzupassen,
|
||||
daß sie bis zur Ausführung von RETRY auch dableiben.
|
||||
|
||||
Noch ein weiteres Konstrukt wurde
|
||||
eingef<EFBFBD>hrt:
|
||||
Noch ein weiteres Konstrukt wurde eingeführt:
|
||||
|
||||
: name ..<clause1>..
|
||||
EXITS ..errorhandler.. throw THEN
|
||||
..<clause2>. ;
|
||||
|
||||
Es tr„gt der Idee Rechnung, daá
|
||||
ERRORHANDLER des ”fteren nur installiert
|
||||
wird, um eine Ressource zu schlieáen.
|
||||
Die Bedeutung: Es wird ein Wort NAME
|
||||
definiert. Bei Ausf<73>hrung f<>hrt NAME
|
||||
erst <clause1> aus, installiert
|
||||
anschlieáend einen ERRORHANDLER, f<>hrt
|
||||
dann <clause2> aus. ERRORHANDLER wird
|
||||
im Falle eines Fehlers oder nach
|
||||
Verlassen des Wortes NAME ausgef<65>hrt.
|
||||
Es lassen sich also so sch”ne Konstrukte
|
||||
Es trägt der Idee Rechnung, daß ERRORHANDLER des öfteren nur
|
||||
installiert wird, um eine Ressource zu schließen. Die Bedeutung: Es
|
||||
wird ein Wort NAME definiert. Bei Ausführung führt NAME erst <clause1>
|
||||
aus, installiert anschließend einen ERRORHANDLER, führt dann <clause2>
|
||||
aus. ERRORHANDLER wird im Falle eines Fehlers oder nach Verlassen des
|
||||
Wortes NAME ausgeführt. Es lassen sich also so schöne Konstrukte
|
||||
bilden wie:
|
||||
|
||||
: machWas ...
|
||||
..<”ffne-Ger„t1>..
|
||||
EXITS ..<schlieáe-Ger„t1>.. throw THEN
|
||||
..<”ffne-Ger„t2>..
|
||||
EXITS ..<schlieáe-Ger„t2>.. throw THEN
|
||||
..<öffne-Gerät1>..
|
||||
EXITS ..<schließe-Gerät1>.. throw THEN
|
||||
..<öffne-Gerät2>..
|
||||
EXITS ..<schließe-Gerät2>.. throw THEN
|
||||
.... ;
|
||||
|
||||
Es werden auf jeden Fall die ge”ffneten
|
||||
Ger„te wieder geschlossen, ob nun ein
|
||||
Fehler auftritt oder nicht.
|
||||
Es werden auf jeden Fall die geöffneten Geräte wieder geschlossen, ob
|
||||
nun ein Fehler auftritt oder nicht.
|
||||
|
||||
o Was gibt es noch f<>r Ans„tze in Forth
|
||||
* Was gibt es noch für Ansätze in Forth
|
||||
|
||||
Der vorgestellte Ansatz bringt nichts
|
||||
prinzipiell neues. In ///////Schliesieck
|
||||
l„át sich eine Methode nachlesen, die
|
||||
sicherlich schneller und einfacher
|
||||
implementiert ist, allerdings ver„ndert
|
||||
sie nicht das Verhalten von ABORT",
|
||||
sondern muá mit einem gesonderten Wort
|
||||
aufgerufen werden. Auch sichert sie
|
||||
lediglich den Stackpointer, eventuelle
|
||||
Parameter m<>ssen also 'zu Fuá'
|
||||
gesichert werden. //////weiter....
|
||||
wenn m
|
||||
Der vorgestellte Ansatz bringt nichts prinzipiell neues. In
|
||||
///////Schliesieck läßt sich eine Methode nachlesen, die sicherlich
|
||||
schneller und einfacher implementiert ist, allerdings verändert sie
|
||||
nicht das Verhalten von ABORT", sondern muß mit einem gesonderten Wort
|
||||
aufgerufen werden. Auch sichert sie lediglich den Stackpointer,
|
||||
eventuelle Parameter müssen also 'zu Fuß' gesichert werden.
|
||||
//////weiter.... wenn m
|
||||
|
||||
Sch”ner: statt THROW ein EXIT
|
||||
nehmen. Was passiert bei exit?
|
||||
Bitte nicht IF..ELSE..RETRY
|
||||
Bitte kein FAILS...THEN ohne throw oder
|
||||
so
|
||||
chöner: statt THROW ein EXIT nehmen. Was passiert bei exit? Bitte
|
||||
nicht IF..ELSE..RETRY Bitte kein FAILS...THEN ohne throw oder so
|
||||
|
||||
Wie rauskriegen, welcher Fehler passiert ist?
|
||||
|
0
6502/UltraForth/README.TXT
Executable file → Normal file
0
6502/UltraForth/README.TXT
Executable file → Normal file
Loading…
Reference in New Issue
Block a user