Das Schneider CPC Systembuch

Grundlagen

Befehls-Elemente

Um 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
Die Bildausgabe: Tinten und Farben
Farbe
(optional) | | ----------+-------------------+---------------------+--------------------------- Ausgaben: | --/-- | Sinus des Winkels | 180 ----------+-------------------+---------------------+---------------------------

Commands

Es 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
MAIN FIRMWARE JUMPBLOCK: SOUND MANAGER
Die Firmware des Schneider CPC: SOUND MANAGER
SOUND
Erklärung der Anschlussbelegung: A, B, Ca,b,c,d,LOW KERNEL JUMPBLOCK: 000E: LOW PCBC INSTRUCTION
LOW KERNEL JUMPBLOCK: 001E: LOW PCHL INSTRUCTION
e
PLOT Die verwendeten Abkürzungen bedeuten: x:x,y PRINT "hallo" CLEAR

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

Statements

Das 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
LOW KERNEL JUMPBLOCK: 001B: LOW KL FAR PCHL
LOW KERNEL JUMPBLOCK: 003B: LOW EXT INTERRUPT
b
=100*200

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
LOW KERNEL JUMPBLOCK: 001B: LOW KL FAR PCHL
LOW KERNEL JUMPBLOCK: 003B: LOW EXT INTERRUPT
b
nicht deren aktuellen Wert, sondern ihre Adresse an die INPUT- bzw. LET-Behandlungsroutine liefern muss. Die INPUT- bzw. LET-Routine benötigt diese Adresse, um den Inhalt der Unterprogramme: VariablenVariablen verändern zu können:

"eine Zahl bitte:"      @Operationen: BD5B / 349A / 349A:  FLO SUBa          100     200      @LOW KERNEL JUMPBLOCK: 000B:  LOW KL LOW PCHL
LOW KERNEL JUMPBLOCK: 001B: LOW KL FAR PCHL
LOW KERNEL JUMPBLOCK: 003B: LOW EXT INTERRUPT
b
| | | | | +----> INPUT <---+ +-> * <-+ | | | +--> LET <--+

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
LOW KERNEL JUMPBLOCK: 001B: LOW KL FAR PCHL
LOW KERNEL JUMPBLOCK: 003B: LOW EXT INTERRUPT
b

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.

Separatoren

Um 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
LOW KERNEL JUMPBLOCK: 001B: LOW KL FAR PCHL
LOW KERNEL JUMPBLOCK: 003B: LOW EXT INTERRUPT
b
, Die verwendeten Abkürzungen bedeuten: x:x = PLOT 100-20*Operationen: BD5B / 349A / 349A: FLO SUBa,300-30*LOW KERNEL JUMPBLOCK: 000B: LOW KL LOW PCHL
LOW KERNEL JUMPBLOCK: 001B: LOW KL FAR PCHL
LOW KERNEL JUMPBLOCK: 003B: LOW EXT INTERRUPT
b
,Die verwendeten Abkürzungen bedeuten: x:x -------------------------------------- ------------------------

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
LOW KERNEL JUMPBLOCK: 001B: LOW KL FAR PCHL
LOW KERNEL JUMPBLOCK: 003B: LOW EXT INTERRUPT
b
*2 oder: PRINT Operationen: BD5B / 349A / 349A: FLO SUBa*20 b-10 c ^ ^ ^ ^ ^

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

Klammern

Eine besondere Rolle kommt noch den Befehls-Elemente: KlammernKlammern zu, die auf jeden Fall auch als Befehls-Elemente: SeparatorenSeparator wirken.

Einleitung: BASIC
Anhang: Basic
Basic
unterscheidet ganz wesentlich zwischen den Kommandos und den anderen Sprach-Elementen, die Ausgaben machen. Benötigt ein Kommando Basic und Maschinencode: ParameterParameter (Eingaben), so werden diese einfach angehängt. Mehrere Basic und Maschinencode: ParameterParameter müssen durch einen Befehls-Elemente: SeparatorenSeparator getrennt werden:

Einleitung: Sound
MAIN FIRMWARE JUMPBLOCK: SOUND MANAGER
Die Firmware des Schneider CPC: SOUND MANAGER
SOUND
7,100,100 oder: PRINT a-b,b-c;c-d d-e ^ ^ ^ ^ ^

Funktionen, Argumente

Sprach-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
Anhang: Basic
Basic
aber von der Sprachstruktur her nicht denkbar.) Die Fließkomma-Routinen: FunktionenFunktionen haben meist eine Eingabe, wie z.LOW KERNEL JUMPBLOCK: 000B: LOW KL LOW PCHL
LOW KERNEL JUMPBLOCK: 001B: LOW KL FAR PCHL
LOW KERNEL JUMPBLOCK: 003B: LOW EXT INTERRUPT
B
. SIN(winkel). Das ist aber nicht unbedingt der Fall. Es gibt auch Die Fließkomma-Routinen: FunktionenFunktionen ohne Eingabe oder mit zwei und noch mehr:

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
Erklärungen zu den Anschlussbezeichnungen: READY
Ready

Felder, Indizees

Zweites Einsatzgebiet für Befehls-Elemente: KlammernKlammern sind die dimensionierten Felder. Das sind strukturierte Variablen, 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
LOW KERNEL JUMPBLOCK: 001B: LOW KL FAR PCHL
LOW KERNEL JUMPBLOCK: 003B: LOW EXT INTERRUPT
b
(1,5) <-- zwei Indizees

Weitgehend unbekannt ist, dass die Programmierer des Locomotive Einleitung: BASIC
Anhang: Basic
Basic
wohl überzeugte PASCAL-Anwender waren. Sie haben nämlich eine Pascal-typische Eigenheit mit in's Schneider-Basic gerettet: Wie in Übergabe von Argumenten und Ergebnissen: PASCAL:Pascal kann man in diesem Basic-Dialekt die Indizees einer Feld-Variablen in eckige Befehls-Elemente: KlammernKlammern einschließen:

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, Operanden

Und, 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
Erklärungen zu den Anschlussbezeichnungen: READY
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:
Die Kodierung der Tintennummern in den Bildschirm-Bytes: Mode 1:
Die Kodierung der Tintennummern in den Bildschirm-Bytes: Mode 0:
MOD
6. Strichrechnung + und -
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
Anhang: Basic
Basic
das Vorzeichen '-' und die Komplement-Bildung 'NOT' wie Operatoren behandelt (Deshalb braucht das Argument von 'NOT' auch nicht in Befehls-Elemente: KlammernKlammern zu stehen.).

Ü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 Vergleich
IF 10+20 > 30+40 ...    --->   (10+20) > (30+40)
                        --->     30    >   70
                        --->        0    (false)
unsinnige Priorität: Vergleich vor Addition
IF 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
Die Firmware des Schneider CPC: CASSETTE MANAGER
ca
.) 2 3200 li subtrahiere 3200-2=3198 3198 10 push 10 10 10 3198 20 push 20 20 20 10 3198 * multipliziere 20*10=200 200 3198 30 push 30 30 30 200 3198 / dividiere 200/30=7 (MAIN FIRMWARE JUMPBLOCK: CASSETTE MANAGER
Die Firmware des Schneider CPC: CASSETTE MANAGER
ca
.) 7 3198 40 push 40 40 40 7 3198 * multipliziere 40*7=280 280 3198 > vergleiche 3198>280=true (-1) -1 . drucke -- -- leer --

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ücke

Wenn 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)

Valid HTML   Valid CSS