Befehls-ElementeUm die Arbeitsweise eines Compilers oder Interpreters zu verstehen, ist es besonders wichtig, sich mit den einzelnen Elementen auseinanderzusetzen, aus denen sich ein kompletter Befehl zusammensetzt. Die Aussage "Das Programm wird Befehl für Befehl abgearbeitet" ist ja noch leicht zu machen. Wie aber, verflixt noch mal, kann der Basic-Interpreter so komplizierte Anweisungen wie die folgende verstehen: PLOT 100*(sin(PI*r/180)+o),100-50*cos(delta),3 Was unterscheidet beispielsweise das Wort 'PLOT' von 'SIN' oder '180'? Gemein ist den drei Zeichenfolgen, dass sie jeweils ein nicht mehr weiter teilbares Element der Sprache darstellen. Um die Unterschiede herauszuarbeiten, kann man sich vielleicht einmal ihre 'Schnittstellen'-Beschreibung ansehen: | PLOT | SIN | 180 ----------+-------------------+---------------------+--------------------------- Die Fließkomma-Routinen: FunktionenFunktion: | Setze Punkt im | Berechne Sinus | Zahlenwert | Bildschirm | | ----------+-------------------+---------------------+--------------------------- Eingaben: | X-Koordinate | Winkel | --/-- | Y-Koordinate | | | Die Grafik: Farben CommandsEs fällt auf, dass 'PLOT' keine Ausgaben macht und '180' keine Eingaben benötigt. Tatsächlich machen alle Worte, mit denen ein Basic-Befehl anfangen kann, keine Ausgaben, die von einem anderen Befehlswort als Eingabe verwendet werden kann: Einleitung: Sound Die Bezeichnung für solche Sprachelemente ist leider nicht ganz einheitlich. Sie sollen aber im Folgenden Befehls-Elemente: CommandsCOMMAND (Befehl, Kommando) genannt werden. Das Kommando 'PLOT' darf nicht mit dem vollständigen Befehl 'PLOT_x,y' verwechselt werden. Andererseits benötigen die meisten Elemente aber Eingaben. Woher kommen die? Von anderen Worten, die eben Ausgaben machen können, wie 'SIN' oder '180'. Aber auch hier gibt es wieder Unterschiede: Während die '180' so etwas wie ein Deckel auf dem Topf ist, benötigt 'SIN' nun seinerseits selbst wieder eine Eingabe. Man kann sich hier recht gut vorstellen, wie einzelne Daten an verschiedenen Quellen herausstroemen, durch verarbeitende Elemente hindurchfließen und schließlich alle in ein gemeinsames Ziel hineinmünden. Dabei gibt es normalerweise beliebig viele Datenquellen, die immer zu einem Ziel hinfließen. Das folgende Diagramm soll die Datenbewegungen in dem komplizierten PLOT-Befehl von oben veranschaulichen: PLOT 100*(sin(PI*r/180)+o),100-50*cos(delta),3 100 PI r 180 o 100 50 delta 3 | | | | | | | | | | +-> * <-+ | | | | cos | | | | | | | | | | +--> / <----+ | | +-> * <--+ | | | | | | | | sin | +----> - <---+ | | | | | | | +----> + <-----+ | | | | | | +---> * <---------------+ | | | | | +------------------------------------> PLOT <---------------+ StatementsDas Kennzeichen eines vollständigen Befehls ist, dass er bezüglich der Parameter: Parameter-ÜbergabeParameter-Übergabe in sich abgeschlossen ist. Er benötigt keine Eingaben von außen und er macht keine Ausgaben an andere Sprach-Elemente. Ein solcher, in sich abgeschlossener Befehl wird Befehls-Elemente: StatementsSTATEMENT genannt. Bei der Definition von 'Eingabe' und 'Ausgabe' in diesem Sinn muss man aufpassen. Hierbei sind nur die Datenwege innerhalb des Befehls-Elemente: StatementsStatements gemeint, die Weiterverarbeitung von Ausgaben, die andere Befehlselemente gemacht haben. Nur um sie muss sich der PARSER der jeweiligen Programmiersprache kümmern. Der Parser ist die Abteilung, die den Programmtext einliest, untersucht und entsprechende Aktionen veranlasst. Auch die beiden folgenden Texte stellen korrekt gebildete Befehls-Elemente: StatementsStatements dar: INPUT "ein Zahl bitte:",Operationen: BD5B / 349A / 349A: FLO SUBa
LET LOW KERNEL JUMPBLOCK: 000B: LOW KL LOW PCHL
Hierbei muss der Parser aber ein gewisses Maß an Intelligenz besitzen, da er für die Unterprogramme: VariablenVariablen Operationen: BD5B / 349A / 349A: FLO SUBa und LOW KERNEL JUMPBLOCK: 000B: LOW KL LOW PCHL "eine Zahl bitte:" @Operationen: BD5B / 349A / 349A: FLO SUBa 100 200 @LOW KERNEL JUMPBLOCK: 000B: LOW KL LOW PCHL Dabei ist die Vorstellung falsch, dass beispielsweise das Befehls-Elemente: StatementsStatement mit INPUT von der Eingabe her nicht abgeschlossen sei, weil der Anwender hier ja eine Eingabe machen müsste: "eine Zahl bitte:" @Operationen: BD5B / 349A / 349A: FLO SUBa [Eingabe von außen] FALSCH!!!!
| | |
+-------> INPUT <-------------+
Um diese Eingabe von außen muss sich nämlich nicht der PARSER kümmern, das ist die Aufgabe der INPUT-Behandlungsroutine. Genauso falsch ist die Vorstellung, dass INPUT bzw. LET eine Ausgabe an weitere Programm-Elemente, nämlich die Unterprogramme: VariablenVariablen machen würden: "eine Zahl bitte:" 100 200 | | | FALSCH!!!! INPUT +-> * <-+ | | Operationen: BD5B / 349A / 349A: FLO SUBa LET | LOW KERNEL JUMPBLOCK: 000B: LOW KL LOW PCHL Auch um diese Zuweisung kümmert sich nicht der Parser, sondern auch wieder die Behandlungsroutine des jeweiligen Befehls. Sonst würden ja beispielsweise auch Befehle wie PLOT oder PRINT Ausgaben machen; nur auf dem Bildschirm eben. Aber diese Ausgaben werden nicht vom Parser an andere Befehlselemente weitergereicht, sondern sind die Aktion, die der Befehl eben bewirken sollte. SeparatorenUm dem Parser überhaupt zu ermöglichen, die einzelnen Sprachelemente zu erkennen, müssen diese voneinander getrennt werden. So ist das folgende Befehls-Elemente: StatementsStatement nicht zu entziffern: PLOTSIN(phi),COS(phi) weil zwischen PLOT und SIN kein Leerzeichen steht. Andererseits führt folgender Variablenname nicht zu einer Fehl-Interpretation: SINUS=0.5 Obwohl 'SINUS' das dem Basic-Parser bekannte Wort 'SIN' enthält, ist es klar als Variablenname erkennbar, weil nach SIN kein Befehls-Elemente: SeparatorenSEPARATOR kommt. Alle Grundlagen: Befehls-ElementeBefehls-Elemente müssen durch Trennzeichen voneinander abgegrenzt werden. Das universellste Zeichen ist dabei der 'SPACE', das Leerzeichen, das wirklich nur zum Trennen von Namen usw. dient und keine eigene Bedeutung hat. Leerzeichen können zwischen den einzelnen Befehlszeichen in beliebiger Menge eingefügt werden: PLOT 100- 20*Operationen: BD5B / 349A / 349A: FLO SUBa , 300-30 * LOW KERNEL JUMPBLOCK: 000B: LOW KL LOW PCHL Demgegenüber haben die Befehls-Elemente: SeparatorenSeparatoren KOMMA, SEMIKOLON, APOSTROPH etc. zusätzliche Bedeutungen: Benötigt ein Grundlagen: Befehls-ElementeBefehls-Element mehrere Eingaben, so müssen diese durch ein Komma getrennt sein. Bei einigen Kommandos gibt es noch Sonder-Regelungen. So verlangt die Wertzuweisung als Befehls-Elemente: SeparatorenSeparator das Gleichheitszeichen '=' und im PRINT-Statement können die einzelnen Argumente mit Leerzeichen, Kommata oder Semikolons abgegrenzt werden. Auf das Apostroph folgt bis zum Zeilenende nur noch nicht auszuwertender Text (eine Bemerkung, Remark). LET Operationen: BD5B / 349A / 349A: FLO SUBa=LOW KERNEL JUMPBLOCK: 000B: LOW KL LOW PCHL Auch die einzelnen Befehls-Elemente: StatementsStatements müssen gegeneinander abgegrenzt werden: Als Grenze gelten dabei der Anfang und das Ende einer Basiczeile und der Doppelpunkt: 10 PRINT Operationen: BD5B / 349A / 349A: FLO SUBa:LET Operationen: BD5B / 349A / 349A: FLO SUBa=Operationen: BD5B / 349A / 349A: FLO SUBa+1:GOTO 10 ^ ^ ^ ^ Einige, spezielle Grundlagen: Befehls-ElementeBefehls-Elemente, die durch ein Sonderzeichen dargestellt werden, werden automatisch als ihr eigener Befehls-Elemente: SeparatorenSeparator erkannt: Plus, Minus, Mal, Geteilt etc.: PRINT 100+200 ist gleichwertig mit PRINT 100 + 200 ^ ^^^ KlammernEine besondere Rolle kommt noch den Befehls-Elemente: KlammernKlammern zu, die auf jeden Fall auch als Befehls-Elemente: SeparatorenSeparator wirken. Einleitung: BASIC Einleitung: Sound
Funktionen, ArgumenteSprach-Elemente wie 'SIN', 'INSTR' oder 'LOG', die Ausgaben machen, benötigen auch Basic und Maschinencode: ParameterParameter. Hier müssen sie aber durch Befehls-Elemente: KlammernKlammern eingeschlossen werden. Das muss geschehen, weil sonst Doppeldeutigkeiten entstehen: PRINT SIN 100 + 200 = PRINT SIN (100+200) oder PRINT SIN(100) + 200 ?? ------------------- ------------------- -------------------- Solche Elemente, die EINE Ausgabe liefern, werden als Die Fließkomma-Routinen: FunktionenFUNKTIONEN bezeichnet. (Prinzipiell sind zwar auch Die Fließkomma-Routinen: FunktionenFunktionen möglich, die mehr als eine Ausgabe machen. Diese sind in Einleitung: BASIC PRINT RND oder PRINT INSTR(5,"1234567","4"). ^ ^ In diesem Zusammenhang ist es interessant, dass die boolsche Die Fließkomma-Routinen: FunktionenFunktion 'NOT' im Schneider-Basic ihr Argument NICHT in Befehls-Elemente: KlammernKlammern erwartet: PRINT NOT 0 [ENTER]
-1
Erklärung zu den Bezeichnungen: READY
Felder, IndizeesZweites Einsatzgebiet für Befehls-Elemente: KlammernKlammern sind die dimensionierten Felder. Das sind strukturierte Unterprogramme: VariablenVariablen, die mehrere Datenplätze umfassen. Um auf ein bestimmtes Datum zugreifen zu können, muss noch ein Erklärungen zu den Anschlussbezeichnungen: INDEXIndex oder mehrere Indizees (je nach Dimensionierung) angegeben werden. Diese werden, gerade wie bei Die Fließkomma-Routinen: FunktionenFunktionen, von Befehls-Elemente: KlammernKlammern umschlossen an den Variablennamen angehängt und, bei mehreren Indizees, durch Kommata getrennt: Die Fließkomma-Routinen: FunktionenFunktion Datenfeld ---------------- ------------ ein Arg.: PRINT SIN(0) PRINT Operationen: BD5B / 349A / 349A: FLO SUBa(7) <-- ein Erklärungen zu den Anschlussbezeichnungen: INDEXIndex zwei Arg.: PRINT Erklärung der Anschlussbelegung: TestTEST(10,0) PRINT LOW KERNEL JUMPBLOCK: 000B: LOW KL LOW PCHL Weitgehend unbekannt ist, dass die Programmierer des Locomotive Einleitung: BASIC PRINT Operationen: BD5B / 349A / 349A: FLO SUBa[1,2]
Damit ist es rein optisch leichter möglich, Zahlenfelder und Die Fließkomma-Routinen: FunktionenFunktionen auseinanderzuhalten. Operatoren, OperandenUnd, um dem Parser das Leben nicht allzu leicht zu machen, gibt es noch ein drittes Anwendungsgebiet für die runden Befehls-Elemente: KlammernKlammern: Prioritätssteuerung in arithmetischen Ausdrücken. Die haben es nämlich 'echt in sich'. Schuld daran ist die von uns Mittel-Europäern favorisierte OPERATOREN-Schreibweise: PRINT 50+60*70 [ENTER]
4250
Erklärung zu den Bezeichnungen: READY
Um Formeln in dieser, uns ach so vertrauten Art in den Computer eingeben zu können, muss der Parser komplizierte Klimmzüge vollbringen (Dieser Wunsch führte in den Gründerjahren des Computer-Zeitalters sogar zur Entwicklung einer eigenen Sprache: FORTRAN = formula transfer). Erst einmal muss der ganze Ausdruck in seine Elemente zerlegt werden. Das geht noch vergleichsweise einfach, dafür sind ja die Befehls-Elemente: SeparatorenSeparatoren da: PRINT 50 + 60 * 70 ----- -- - -- - -- 'Plus' und 'Mal' sind hier Operatoren. Alle Die Fließkomma-Routinen: OperationenOperationen sind zweiwertige Die Fließkomma-Routinen: FunktionenFunktionen. das heißt, sie benötigen zwei Argumente (die Operanden genannt werden) und liefern einen Funktionswert: 60 * 70 entspricht MULT(60,70) Nun hat unsere auf den ersten Blick so bestechende Operatoren-Schreibweise einen gewaltigen Haken: Sie ist NICHT EINDEUTIG: PRINT 50+60*70 = PRINT (50+60)*70 oder PRINT 50+(60*70) ??? Tscha, und genau dafür braucht man die Befehls-Elemente: KlammernKlammern, wie man sieht. Sie legen in einem arithmetischen Ausdruck die Rechen-Reihenfolge fest: 50 60 70 50 60 70 | | | | | | +-> + <-+ | | +-> * <-+ | | | | +--> * <-+ +--> + <--+ | | PRINT PRINT Aber, um die Tasten mit der Klammer-Auf und -Zu zu schonen, hat man sich schon sehr früh einen zusätzlichen Trick einfallen lassen: Die verschiedenen Operatoren erhalten eine PRIORITAET zugeordnet. Ohne eine solche Priorität würde jeder komplexere Ausdruck von links nach rechts ausgewertet, wenn keine Klammer etwas anderes befiehlt: 10 + 20 * 30 / 40 - 50 = (((10+20) *30) /40) -50 Die verschiedenen Prioritäten schlagen sich beispielsweise in so schönen Regeln wie "Punktrechnung vor Strichrechnung" nieder, und verändern die Reihenfolge der Auswertung: 10 + 20 * 30 / 40 - 50 = (10 + ((20*30) / 40)) - 50 Nun gibt es aber noch eine Reihe weitere Operatoren, die vom Schneiderschen Basic-Interpreter folgende Prioritäts-Hierarchie zugeordnet bekommen: I) 1. Potenzierung ^
2. Vorzeichenwechsel -
3. Punktrechnung * und /
4. Integerdivision \
5. Restbildung Die Kodierung der Tintennummern in den Bildschirm-Bytes: Mode 2:
II) 7. Vergleich < > <= >= <> und = III) 8. Komplement NOT 9. Und AND 10. Oder OR 11. Exclusiv-Oder XOR Es ist interessant festzuhalten, dass Locomotive Einleitung: BASIC Übrigens ist auch die String-Verknüpfung eine Die Fließkomma-Routinen: OperationenOperation: PRINT Operationen: BD5B / 349A / 349A: FLO SUBa$ + "abc"
Da es für Datentypen: StringsStrings aber nur diese einzige Die Fließkomma-Routinen: OperationenOperation gibt, braucht der Parser hier keine Prioritäten auszuwerten. Bei den arithmetischen Operatoren lassen sich drei Gruppen unterscheiden, die in der Tabelle mit I, II und III markiert sind. Eine Die Fließkomma-Routinen: OperationenOperation von Gruppe I kann nur eine Priorität innerhalb dieser Gruppe zugeordnet bekommen. Es ist beispielsweise unsinnig, die Rechnen im Binärsystem: MultiplikationMultiplikation zwischen 'AND' und 'OR' einzuordnen. Das liegt am Typ der Eingaben, die eine Die Fließkomma-Routinen: OperationenOperation erwartet und am Typ ihrer Ausgabe: Alle Die Fließkomma-Routinen: OperationenOperationen der Gruppe I benötigen als Eingabe einen numerischen Wert und liefern auch einen solchen als Ausgabe. Die Vergleiche (Gruppe II) benötigen als Eingaben auch numerische Werte, liefern aber boolsche Ausdrücke als Ausgabe. Und die boolschen Operatoren der Gruppe III schließlich erwarten und liefern boolsche Werte. I: numerisch ---> numerisch II: numerisch ---> boolean III: boolean ---> boolean Das folgende Beispiel zeigt, dass ein Vermischen der Gruppen nur Unsinn liefern kann: sinnvolle Priorität: Addition vor VergleichIF 10+20 > 30+40 ... ---> (10+20) > (30+40) ---> 30 > 70 ---> 0 (false) unsinnige Priorität: Vergleich vor AdditionIF 10+20 > 30+40 ... ---> (10 + (20>30)) + 40 ---> (10 + false) + 40 ---> ????? 10 und die logische Aussage 'false' (nein, falsch) können nicht addiert werden. Die Rechnen im Binärsystem: AdditionAddition erwartet zwei Zahlen als Eingabe. In den meisten Basic-Versionen gelänge die Rechnen im Binärsystem: AdditionAddition aber trotzdem. Beispielsweise auch beim Schneider CPC. Dessen Basic-Interpreter unterscheidet nämlich nicht zwischen Zahlen und boolschen Aussagen, sondern assoziiert 'false' mit '0' und 'true' mit '-1' oder jeder anderen Zahl ungleich Real: NullNull. Nur sinnvoll wäre das Ergebnis in den allermeisten Fällen nicht. Im Gegensatz dazu lässt Übergabe von Argumenten und Ergebnissen: PASCAL:PASCAL solchen Unsinn erst gar nicht zu. Wie geht nun aber ein Parser vor, um die verschiedenen Die Fließkomma-Routinen: OperationenOperationen in der korrekten Reihenfolge zu erledigen? Ein möglicher Weg wäre, zuerst den gesamten Ausdruck in seine Elemente zu zerlegen und dann den Ausdruck mit der höchsten Priorität nach Operatoren beginnend zu durchsuchen und diesen jeweils mit seinen Operanden zu Befehls-Elemente: Klammernklammern: PRINT 100* 2^5 - 30/55 > 10*20 /30 *40 ----> (((100*(2^5))-(30/55))>(((10*20)/30)*40)) suche ^ ----> ||| +---+| | || ||| | | || suche */ ----> ||+---------+ | || ||| | | || suche */ ----> || +-----+| ||| | | || suche */ ----> || | ||+-----+ | || suche */ ----> || | |+----------+ || suche */ ----> || | +---------------+| suche +- ----> |+-------------------+ | suche vgl. ----> +---------------------------------------+ Allgemeine ist ein solcher TERM (arithmetischer Ausdruck) wie folgt aufgebaut:
Weil ein Term immer wieder auch aus untergeordneten Termen bestehen kann, ist eine Auswertung fast nur mittels Rekursion möglich. Dabei wird ein Stapel benötigt, auf dem die Zwischenergebnisse (Idizees, Basic und Maschinencode: ParameterParameter oder Operanden) festgehalten werden können. Dabei ist die Sprache Übergabe von Argumenten und Ergebnissen: FORTH:FORTH sehr gut geeignet, das gewünschte Ziel einer solchen maschinengerechten Umwandlung zu zeigen. In Übergabe von Argumenten und Ergebnissen: FORTH:FORTH nehmen alle Die Fließkomma-Routinen: FunktionenFunktionen etc. ihre Basic und Maschinencode: ParameterParameter von einem Zahlenstapel und legen ihre Ergebnisse auch wieder darauf ab. Das obige Basic-Beispiel würde hier wie folgt übersetzt: PRINT 100 * 2^5 - 30 / 55 > 10 * 20 / 30 * 40 Befehl Aktion in Zahlen Inhalt des Stapels ------ --------- --------- ------------------ 100 push 100 100 100 2 push 2 2 2 100 5 push 5 5 5 2 100 ^ potenziere 2^5=32 32 100 * multipliziere 32*100=3200 3200 30 push 30 30 30 3200 55 push 55 55 55 30 3200 / dividiere 55/30=2 (MAIN FIRMWARE JUMPBLOCK: CASSETTE MANAGER Nun ist das oben vorgeschlagene Verfahren (alle Operatoren in der Reihenfolge ihrer Priorität suchen) zwar sehr galant, aber leider nur für den menschlichen Betrachter, der den Die Firmware des Schneider CPC: ÜberblickÜberblick hat. Den für einen Parser zu simulieren, bringt leider recht umfangreiche String-Operationen mit sich. Für den Computer ist es immer leicheter, etwas der Reihe nach zu bearbeiten. Auswerten arithmetischer AusdrückeWenn man sich aber die FORTH-Umsetzung dieses komplizierten Terms anschaut, so sieht man, dass ein solches lineares Vorgehen durchaus möglich ist: Alle Zahlen tauchen hier in der selben Reihenfolge auf, wie sie im Basic-Ausdruck vorlagen. Nur die Operatoren können erst ausgeführt werden, wenn die Operanden beide auf dem Stapel vorliegen und, wichtig!!, niederwertige Die Fließkomma-Routinen: OperationenOperationen werden nicht sofort ausgeführt, wenn ein höherwertiger Operator folgt. Daraus ergibt sich ein Lösungs-Algorithmus, bei dem man bei Bedarf niederwertige Operatoren auf einen Stapel zwischenspeichern kann. Folgender Ausdruck müsste wie folgt umgesetzt werden: 2+3*4^5 ---> 2 3 4 5 ^ * + Hier hat nicht nur das '*' die sofortige Ausführung von '+' verhindert. Weil auch nach '*' noch eine höhere Die Fließkomma-Routinen: OperationenOperation folgte '^', wurde auch diese noch vorgezogen. Während der Analyse eines Ausdrucks wird dabei der Operatoren-Stack benötigt. Während der Ausführung (Berechnung) der Zahlenstapel. Der Basic-Interpreter, der beides gleichzeitig besorgt, muss mit beiden Stapeln gleichzeitig hantieren. Das folgende Beispiel soll den Lösungs-Algorithmus beschreiben. Als einfachstes Beispiel sind nur Zahlen und Operatoren erlaubt: Auswerten arithmetischer Ausdrücke (einfachst)[eval] LEGE Abschlussmarke {Null-Operation} auf den Operatorenstapel ab. [eval0] HOLE Zahl und LEGE diese auf dem Variablenstapel ab. WENN kein Operator mehr folgt DANN {ist der Ausdruck hier zuende.} SOLANGE auf dem Operatorenstapel noch Operatoren liegen: RUFE execut. HOLE Abschlussmarke wieder vom Operatorenstapel weg. FERTIG. SONST HOLE den Operator. SOLANGE die Priorität dieses Operators nicht höher ist, als die Priorität des letzten Operators im Operatorenstapel: RUFE execut. LEGE Operator auf dem Operatorenstapel ab. MACHE bei eval0 weiter. [execut] HOLE Operator vom Operatorenstapel und HOLE zwei Operanden vom Variablenstapel und RUFE Operator-Behandlungs-Routine auf und LEGE Ergebnis wieder auf dem Variablenstapel ab. FERTIG. Dieses Beispiel zeigt, wie man einen 'Programm-Ablaufplan' auch in klar verständliche, deutsche Saetze fassen kann. Diese Methode hat den Vorteil, dass man seine Gedanken auch mit Hilfe einer Textverarbeitung erfassen und ordnen kann. Bei seiner eigenen 'Kunstsprache' kann man sich sogar den Luxus leisten, und den Einrückungen eine syntaktische Bedeutung zukommen lassen. So zum Beispiel für den Geltungsbereich einer WENN/DANN/SONST-Entscheidung oder einer SOLANGE-Schleife. Zum besseren Verständnis dieser, meiner eigenen Version folgende 'Übersetzungstabelle': [...] - Sprungmarke (Label) {...} - Bemerkung (Remark) RUFE - Die CPU Z80: Unterprogramm-AufrufeUnterprogramm-Aufruf (Maschinencode über HIMEM: CALLCALL, GOSUB) FERTIG - Unterprogramm-Rücksprung (RET, RETURN) WENN \ DANN >Bedingte Bearbeitung von (IF-THEN-ELSE) SONST / Befehlssequenzen SOLANGE - Schleife (WHILE) |