D I S S E R T A T I O N DEFINITION EINER ERWEITERBAREN HOEHEREN PROGRAMMIERSPRACHE ausgefuehrt zum Zwecke der Erlangung des akademischen Grades eines Doktors der technischen Wissenschaften eingereicht an der Technischen Universitaet Wien (Technisch Naturwissenschaftliche Fakultaet) von Dipl.-Ing. Thomas Mertes 1238 Wien, Kroissberggasse 25 geboren am 16. 10. 1960 in Wien Wien, 1986 01 10 K U R Z F A S S U N G ====================== In herkoemmlichen Programmiersprachen ist die Form der Anwei- sungen, Operatoren, Deklarationen, Prozeduren und Funktionen in der Sprachdefinition festgelegt. Diese Vorgangsweise erleich- tert die Uebertragung von Programmen von einem Rechner auf den anderen, erschwert aber die Anpassung der Programmiersprache an spezielle Anwendungsgebiete. Um Anpassungen durchzufuehren wur- den bisher Voruebersetzer, Makroprozessoren und Compilergenera- toren verwendet. Die in dieser Arbeit definierte erweiterbare Programmier- sprache ermoeglicht die Anpassung an verschiedene Anwendungsge- biete dadurch, dass Anweisungen, Operatoren und Deklarations- konstrukte vom Anwender definiert werden koennen. Anweisungen und Operatoren koennen vereinbart werden, indem bei der Dekla- ration von Prozeduren und Funktionen die Syntax ihrer Aufrufe festgelegt wird. Es werden also Anweisungen und Operatoren als Aufrufe von Unterprogrammen aufgefasst. Mit Hilfe eines Over- loading-Konzeptes und der Moeglichkeit, Operatorprioritaeten und Assoziativitaeten zu vergeben, kann man Konstrukte ver- einbaren, die mit den ueblichen Notationen eines Anwendungsge- bietes uebereinstimmen. Durch ein hierarchisches Typkonzept, in dem der Anwender selbst die Kompatibilitaeten zwischen den Ty- pen bestimmen kann, wird erreicht, dass ein falsches Verwenden von Konstrukten schon zur Uebersetzungsteit erkannt werden kann. Ein logisch aufgebautes System fuer Module und abstrakte Datentypen sorgt dafuer, dass auch groessere Programme in uebersichtlicher Weise geschrieben werden koennen. Die Ein- Ausgabe unterstuetzt sowohl zeichenweise interaktive Eingabe mit und ohne Terminalecho, als auch eine LL(1)-Analyse. Zusam- menfassend kann man sagen, dass die unter dem Gesichtspunkt voelliger implementierungsunabhaengigkeit definierten Konstruk- te, dieser erweiterbaren Programmiersprache eine aussergewoehn- liche Maechtigkeit verleihen. Die zur Analyse der Sprache vorgeschlagene datenstrukturgesteuerte LL(1)-Analyse wird ab- skchliessend an Hand von Beispielen erlaeutert. Weitere Moeglichkeiten, wie zum Beispiel eine Behandlung von Exceptions oder parallele Prozesse, koennen durch die Aus- nutzung der Erweiterbarkeit grossteils in der Programmier- sprache selbst definiert werden. Die angefuehrten Vorteile wer- den durch einen langsameren Ablauf der Programme erkauft. I N H A L T S V E R Z E I C H N I S =================================== Kapitel Inhalt Seite 1. E I N L E I T U N G 1 2. B E S C H R E I B U N G V O N M A S T E R 5 2.1 ALLGEMEINES 5 2.2 VORDEFINIERTE ANWEISUNGEN 6 2.2.1 Zuweisung 6 2.2.2 WHILE-Anweisung 6 2.2.3 REPEAT-Anweisung 7 2.2.4 FOR-Anweisung 7 2.2.5 FOR-VAR-Anweisung 8 2.2.6 IF-Anweisung 9 2.2.7 CASE-Anweisung 10 2.2.8 BEGIN-Anweisung 10 2.2.9 LOCAL-Anweisung 11 2.3 TYPEN 13 2.3.1 Vordefinierte Typen 13 2.3.1.1 BOOLEAN 13 2.2.1.2 INTEGER 14 2.2.1.3 CARDINAL 15 2.2.1.4 RATIONAL 16 2.2.1.5 REAL 16 2.2.1.6 CHAR 17 2.2.1.7 STRING 18 2.2.1.8 ENUMERATION 19 2.2.1.9 SET 20 2.2.1.10 POINTER 22 2.2.1.11 ARRAY 22 2.2.1.12 RECORD 23 2.2.1.13 UNION 23 2.2.1.14 TYPE 24 2.2.1.15 PROC 24 2.2.1.16 PROC RETURN xtyp 24 2.3.2 Anwenderdefinierte Typen 25 2.3.3 Kompatibilitaet von Typen 26 2.3.4 Deklaration voll kompatibler Typen 27 2.3.5 Deklaration abwaertskompatibler Typen 28 2.3.6 Deklaration nicht kompatibler Typen 28 2.3.7 Deklaration von Strukturtypen 30 2.3.8 Teilbereichstypen 31 2.3.8.1 Subrange-Typen 31 2.3.8.2 Array-Typen 31 2.3.9 Explizite Typkonversion 31 2.4 PROZEDUREN UND ANWEISUNGEN 32 2.4.1 Normale Parameter 32 2.4.1.1 CONST-Parameter 32 2.4.1.2 IN-Parameter 33 2.4.1.3 VAR-Parameter 34 2.4.1.4 OUT-Parameter 34 2.4.1.5 RETURN-Parameter 35 2.4.1.6 Bedingungen fuer die Parameteruebergabe 36 2.4.2 Symbol-Parameter 37 2.4.3 Struktur-Parameter 37 2.4.3.1 SEQUENCE-Parameter 38 2.4.3.2 OPTIONAL-Parameter 40 2.4.3.3 ONEOF-Parameter 41 2.4.4 LOCAL-Parameter 43 2.4.5 Initialisierungsparameter 44 2.5 OPERATOREN UND FUNKTIOEN 46 2.5.1 Funktionen 46 2.5.2 Antifunktionen 47 2.5.3 Hybridfunktionen 48 2.5.4 Overloading 50 2.5.5 Operatoren 51 2.5.6 Klammern 55 2.5.7 Ausdruecke 55 2.6 MODULE 59 2.6.1 Einfache Module 59 2.6.2 Qualifiziert exportierende Module 60 2.6.3 Gemischt exportierende Module 62 2.7 ABSTRAKTE DATENTYPEN 64 2.7.1 Einfache abstrakte Datentypen 64 2.7.2 Abstrakte Datentypen mit Parametern 66 2.7.3 Unqualifiziert exportierende abstrakte Datentypen 68 2.7.4 Attribute 69 2.8 EIN- AUSGABE 71 2.8.1 Textfiles 72 2.8.1.1 Das Textfile OUTPUT 73 2.8.1.2 Das Textfile KEYBOARD 73 2.8.1.3 Das Textfile INPUT 74 2.8.1.4 Prozeduren fuer Textfiles 74 2.8.1.5 Die Prozedur WRITE 75 2.8.1.6 Die Prozedur WRITELN 76 2.8.1.7 Die Prozedur READ 76 2.8.1.8 Die Prozedur READWD 77 2.8.1.9 Die Prozedur READLN 78 2.8.1.10 Die Prozedur PAGE 79 2.8.1.11 Die Prozedur GOTOXY 79 2.8.1.12 Syntaxanalyse mit Textfiles 79 2.8.2 Datenfiles 80 2.9 PROZEDURTYPEN 81 3. F O R M A L E B E S C H R E I B U N G 86 3.1 ALLGEMEINES 86 3.2 DIE ERWEITERTE BACKUS-NORMALFORM 87 3.3 BESCHREIBUNG VON MASTER IN BACKUS-NORMALFORM 88 3.4 BEISPIELE 93 3.4.1 Die Anweisungen WHILE, REPEAT, BEGIN und IF 93 3.4.2 Der Typ CHAR 95 3.5 DER ZEICHENSATZ 99 4. I M P L E M E N T I E R U N G 100 4.1 ALLGEMEINES 100 4.2 ANALYSE 101 4.2.1 Symbolanalyse 101 4.2.2 Ausdruckanalyse 103 4.2.3 Darstellung von Ausdruecken 111 5. S T I C H W O R T V E R Z E I C H N I S 112 6. L I T E R A T U R V E R Z E I C H N I S 114 - 1 - 1. E I N L E I T U N G ====================== Herkoemmliche Programmiersprachen haben einen fest vorgegebe- nen syntaktischen Aufbau. Die Form der Anweisungen, Operatoren, Deklarationen, Prozeduren und Funktionen ist in der Sprachdefi- nition festgelegt und kann vom Benutzer nicht veraendert wer- den. Es ist lediglich moeglich neue Prozeduren, Funktionen und in manchen Sprachen auch neue Operatoren, zu vereinbaren. Die Syntax der Prozedur-, Funktions- bzw. Operatoraufrufe kann je- doch nicht veraendert werden. Dieses starre Schema ist fuer die Uebertragung von Programmen von einem Rechner auf den anderen guenstig, die Weiterentwicklung einer Programmiersprache wird dadurch aber fast unterbunden. Erweiterungen sind aber wuen- schenswert, um bestehende Schwaechen auszubessern, neue ueber- sichtlichere Konstrukte einzufuehren und die Programmiersprache an verschiedene Anwendungsgebiete anzupassen. So kann z.B. im Anwendungsgebiet der Mathematik die Verstaendlichkeit eines Programmes durch Einfuehrung von Operatoren fuer Matrixopera- tionen erheblich gesteigert werden. Nach der Vereinbarung eines In-Produktes und eines Ex-Produktes fuer Vektoren kann man z.B. schreiben v1 := v2 ex v3; write(v1 * v2); Bei Datenbankanwendungen koennen Programme durch die Verwen- dung einer Suchanweisung an Stelle einer Suchprozedur ueber- sichtlicher werden. Ein moeglicher Aufruf einer neu vereinbar- ten Suchanweisung waere: SEARCH (person1.alter=person2.alter) AND (person1.mutter=person2.mutter) AND (person1<>person2) WHEN FOUND WRITE("Zwillinge: ", person1.name, " und ", person2.name); ELSE WRITE("Keine Zwillinge gefunden."); END SEARCH; Durch solche Weiterentwicklungen kann man Programme leichter aendern und darin enthaltene Fehler leichter erkennen. Fuer eine Erweiterung sind neue Syntax- und Semantikdefinitionen der Programmiersprache und eine entsprechende Implementierung er- forderlich. Zur Implementierung der neu entstandenen Programmiersprache gibt es fuenf Moeglichkeiten: 1. Das Schreiben eines Uebersetzers. 2. Das Schreiben eines Voruebersetzers. 3. Die Verwendung eines Makroprozessors. 4. die Verwendung eines Compilergenerators. 5. Die Verwendung einer erweiterbaren Programmiersprache. Das Schreiben eines Uebersetzers ist sehr arbeitsaufwendig und erfordert, auch wenn nur ein bestehender Uebersetzer ge- aendert wird, ein genaues Eingehen auf die Details des zu erzeugenden Codes. - 2 - Das Schreiben eines Voruebersetzers ist weniger arbeitsauf- wendig, fuehrt aber bei haeufigen Aenderungen entweder dazu, dass mehrere Voruebersetzer zur Uebersetzung hintereinander gestartet werden oder viele Aenderungen an einem Voruebersetzer durchgefuehrt werden muessen. Ein Makroprozessor arbeitet flexibel genug, um auch bei haeufigen Aenderungen verwendet werden zu koennen, er kann je- doch die statische Semantik einer Programmiersprache nicht gut ueberpruefen. Ausserdem beziehen sich eventuelle Fehlermeldun- gen des Laufzeitsystems auf das vom Makroprozessor erzeugte Programm und nicht auf das urspruengliche Programm (dasselbe gilt fuer ein von einem Voruebersetzer erzeugtes Programm). Wenn eine formale Syntax- und Semantikbeschreibung einer Pro- grammiersprache vorhanden ist, so kann ein Compilergenerator einen entsprechenden Uebersetzer erzeugen. Die dabei verwende- ten Syntax- und Semantikbeschreibungen basieren auf formalen Beschreibungsmethoden wie z.B. Attributierten Grammatiken [Kastens/Hutt/Zimmermann 1982]. Die Definition einer Program- miersprache mit solch einer Beschreibungsmethode muss sehr de- tailliert sein und wird dadurch relativ lang und unuebersicht- lich. Die Beschreibungssprache ist voellig unterschiedlich zur Programmiersprache und muss daher zusaetzlich erlernt werden. Ausserdem ist ein Generierungslauf aufwendig und sollte deshalb so selten wie moeglich stattfinden. Ein Compilergenerator ist also nicht fuer den Benutzer der Programmiersprache gedacht und wird ihm daher zumeist gar nicht zugaenglich sein. Eine erweiterbare Programmiersprache ist eine Programmier- sprache, in der neue Anweisungen, Operatoren und Deklarations- konstrukte vom Anwender definiert werden koennen. Dadurch wird es moeglich, komplexe Strukturen in einfacher und ueber- sichtlicher Weise darzustellen und die Programmiersprache an verschiedene Anwendungsgebiete anzupassen. Die Einfuehrung neu- er Konstrukte ermoeglicht es also, komplexe Probleme durch Ver- wendung allgemein ueblicher und leicht verstaendlicher Notatio- nen zu ueberschauen. Ausserdem erhoeht sich die Lebensdauer einer Programmiersprache, da neue Ideen nachtraeglich einge- bracht werden koennen. Eine erweiterbare Programmiersprache hat also zwei Zielsetzun- gen: - Es soll moeglich sein, konventionelle Programme in dieser Programmiersprache zu schreiben. - Neue Konstrukte sollen mit Hilfe der erweiterbaren Pro- grammiersprache beschrieben und verwendet werden koen- nen. - 3 - In dieser Arbeit wird die erweiterbare Programmiersprache MASTER definiert. MASTER bedeutet modulare allgemeine strukturierte erweiterbare Sprache. Dieser Programmiersprache liegen folgende Kriterien zugrunde: Orthogonalitaet: Wenn einer Sprache nur wenige, voneinander unabhaengige Prin- zipien, die beliebig kombiniert werden koennen, zugrundeliegen, reicht zum Verstaendnis der Sprache das Erlernen dieser Grund- prinzipien aus. Leichte Uebersetzbarkeit: "A language that is simple to parse for the compiler is also simple to parse for the human programmer and that can only be an asset." Niklaus Wirth [Horowitz 1984] Diese Tatsache erlaubt es, schwer zu analysierende Konstruktio- nem mit der Begruendung zu verbieten, dass sie auch fuer den Menschen schlecht verstaendlich sind. Beschraenkungsfreiheit: Beschraenkungsfreiheit bedeutet, dass die Moeglichkeiten eines Konstrukts nicht durch willkuerliche Grenzen einzu- schraenken sind. Willkuerliche Grenzen sind z.B. eine maximale Laenge der Programmzeilen, eine maximale Laenge der erlaubten Namen, maximale Anzahl von Felddimensionen, maximale Rekur- sionstiefe, usw. Implementierungsunabhaengigkeit: Eine Programmiersprache sollte voellig unabhaengig von einer konkreten Implementierung definiert sein. Erst damit koennen Programme leicht von einem Rechner auf einen anderen ueber- tragen werden. Das erfordert z.b., dass die Sprache einen Implementierungsunabhaengigen Zeichensatz verwendet. Allgemeine Einsetzbarkeit Von Programmiersprachen, die fuer spezielle Anwendungen entwickelt worden sind, wird meist eine Ausweitung ihrer Moeg- lichkeiten gefordert. Es ist daher guenstig, eine Programmier- sprache allgemein einsetzbar zu gestalten, um spaetere Sprach- aenderungen zu vermeiden. Die allgemeine Einsetzbarkeit einer erweiterbaren Programmiersprache ist durch ihre flexible Anpassungsmoeglichkeit automatisch gegeben. - 4 - Moeglichkeit von Subsets: Das Einplanen von Subsets in eine Programmiersprache ermoeg- licht es, fuer einfache Anwendungsgebiete einfache Sprachueber- setzer zu verwenden. Somit erleichtern Subsets den Bau von Uebersetzern und dadurch die allgemeine Verbreitung einer Pro- grammiersprache. Ein moegliches Subset ist z.B. die Pro- grammiersprache MASTER ohne Unterstuetzung der Erweiterbar- keit. Erweiterbarkeit: Dadurch, dass fast alle Konstrukte einer Programmiersprache in der Sprache selbst definiert werden koennen, erhoeht sich ebenfalls die Erlernbarkeit, da es wenige Sonderfaelle gibt. - 5 - 2. B E S C H R E I B U N G V O N M A S T E R ================================================ 2.1 ALLGEMEINES =============== MASTER ist eine Abkuerzung und bedeutet modulare allgemeine strukturierte erweiterbare Sprache. Die Programmiersprache MASTER beruht auf folgenden Prinzipien. Neue Anweisungen werden wie Prozeduren und neue Operatoren wie Funktionen vereinbart. (Es wird also keine Unterscheidung zwischen Prozeduren und Anweisungen, beziehungsweise Operatoren und Funktionen getrof- fen.) Die Syntax eines Prozedur- bzw. Funktionsaufrufes kann bei der Deklaration vom Anwender bestimmt werden. Jedes Objekt (Variable, Konstante, Prozedur, Funktion, Typ) und jeder Aus- druck der Programmiersprache hat genau einen Typ zugeordnet. Bei der Deklaration eines neuen Typs kann der Anwender festle- gen, ob und wie kompatibel der neue Typ zu den bisher gueltigen Typen ist. Die nachfolgende Beschreibung der Programmiersprache MASTER gliedert sich in zwei Teile. Im ersten Teil (Vordefinierte Anweisungen und Vordefinierte Ty- pen) werden die vordefinierten Konstrukte von MASTER so weit beschrieben wie es zur Abfassung einfacher konventioneller Pro- gramme notwendig ist. Die vordefinierten Anweisungen und Typen von MASTER orientieren sich an den Anweisungen und Typen von MODULE [Wirth 1982] und ADA [ANSI 1983]. Die teilweise nur ge- ringen Unterschiede zwischen den vordefinierten Konstrukten von MASTER und MODULA erlauben ein Verstaendnis vieler Programm- beispiele des zweiten Teils der Beschreibung auch ohne Lektuere des ersten Teils der Beschreibung. Im zweiten Teil der Beschreibung (ab: Anwenderdefinierte Ty- pen) wird auf die speziellen Moeglichkeiten einer erweiterbaren Programmiersprache eingegangen. Die vordefinierten Konstrukte muessen in MASTER in Grossbuch- staben geschrieben werden. Um die anwenderdefinierten Konstruk- te hervorzuheben, werden sie in den Beispielen der Beschrei- bung in Kleinbuchstaben geschrieben. Kommentare innerhalb von Programmteilen werden durch Einklam- mern in Kommentarklammern ( (* und *) ) gekennzeichnet. Inner- halb eines Kommentars darf in MASTER jeder Text, der keine Kon- trollzeichen (ausgenommen Zeilenende) enthaelt, vorkommen. Das Schachteln von Kommentaren ist in MASTER ebenfalls erlaubt. - 6 - 2.2 VORDEFINIERTE ANWEISUNGEN ============================= In MASTER sind folgende Anweisungen (statements) vordefiniert: Zuweisung, WHILE-Anweisung, REPEAT-Anweisung, FOR-Anweisung, FOR-VAR-Anweisung, IF-Anweisung, CASE-Anweisung, BEGIN-Anwei- sung, LOCAL-Anwisung und Prozeduraufruf. statement ::= assignment_statement | while_statement | repeat_statement | for_statement | for_var_statement | if_statement | case_statement | begin_statement | local_statement | procedure_call . Fuer die vordefinierten Anweisungen gilt, dass ueberall, wo eine Anweisung stehen kann, auch eine Folge von Anweisungen (statement_sequence) stehen kann. statement_sequence ::= { statement ';' } . Die Anweisungen in einer Anweisungsfolge werden mit Strich- punkt abgeschlossen und nicht durch Strichpunkt voneinander ge- trennt. 2.2.1 Zuweisung z.B.: minimum:=maximum/2; Semantik: Der Ausdruck auf der rechten Seite des Zuweisungssymbols wird ausgewertet und der Variablen auf der linken Seite zu- gewiesen. Syntax: assignment_statement ::= designator ':=' expression . Die Typen des Ausdrucks (expression) und der Variablen (de- signator) muessen kompatibel sein. 2.2.2 WHILE-Anweisung z.B.: WHILE maximum>minimum DO minimum:=2*minimum+schrittweite; schrittweite:=schrittweite-1; END WHILE; Semantik: Die zwischen WHILE und DO stehende Bedingung wird ausgewer- tet. Wenn diese Auswertung FALSE ergibt, so is t die WHILE- Anweisung beendet. Wenn die Auswertung TRUE ergibt, werden - 7 - die zwischen DO und END stehenden Anweisungen durchgefuehrt und die ganze WHILE-Anweisung nochmals ausgefuehrt. Syntax: while_statement ::= 'WHILE' expression 'DO' statement_sequence 'END' 'WHILE' . Der Ausdruck (expression) muss vom Typ Boolean sein. 2.2.3 REPEAT-Anweisung z.B.: REPEAT minimum:=minimum+1; maximum:=maximum-schrittweite; UNTIL 2*minimum>maximum; Semantik: Die zwischen REPEAT und UNTIL stehenden Anweisungen werden durchgefuehrt. Anschliessend wird die nach UNTIL stehende Bedingung ausgewertet. Wenn diese Auswertung TRUE ergibt, so ist die REPEAT-Anweisung beendet. Wenn die Auswertung FALSE ergibt, dann wird die ganze REPEAT-Anweisung noch einmal ausgefuehrt. Syntax: repeat_statement ::= 'REPEAT' statement_sequence 'UNTIL' expression . Der Ausdruck (expression) muss vom Typ Boolean sein. 2.2.4 FOR-Anweisung z.B.: FOR zahl RANGE {minindex..maxindex} AND feld[zahl] <> gesuchter_wert DO summe:=summe+feld[zahl]; END FOR; Semantik: Zuerst wird die nach RANGE stehende Menge (siehe Kapitel 2.3.1.9) ausgewertet. Handelt es sich dabei um die leere Menge, so ist die FOR-Anweisung beendet. Anschliessend er- haelt die nach FOR stehende Kontrollvariable den niedrigsten (beziehungsweise, wenn vor dem Symbol RANGE das Symbol REVERSE steht, den hoechsten) Wert aus der angegebenen Men- ge. Die zwischen DO und END stehenden Anweisungen werden durchgefuehrt. Wenn die Kontrollvariable noch nicht den - 8 - hoechsten (bzw. niedrigsten) Wert der Menge erreicht hat, erhaelt sie den naechsthoeheren (bzw. naechstniedrigeren) Wert aus der Menge und die zwischen DO und END stehende Anweisungsfolge wird erneut durchgefuehrt. Dieser Vorgang wird so lange wiederholt, bis der hoechste (bzw. niedrigste) Wert der Menge erreicht ist. Wenn ein AND-Teil vorhanden ist, so wird die nach And stehende Bedingung nach jeder Zu- weisung an die Kontrollvariable ausgewerted. Ergibt diese Auswertung FALSE, so ist die FOR-Anweisung beendet. Nach Ausfuehrung der FOR-Anweisung hat die Kontrollvariable den letzten ihr zugewiesenen Wert. Syntax: for_statement ::= 'FOR' identifier [ 'REVERSE' ] 'RANGE' set_expression [ 'AND' expression ] 'DO' statement_sequence 'END' 'FOR' . Die Bedingung (expression) muss vom Typ Boolean sein. 2.2.5 FOR-VAR-Anweisung z.B.: FOR VAR cardinal: zahl RANGE {minindex..maxindex} AND feld[zahl] <> gesuchter_wert DO summe:=summe+feld[zahl]; END FOR; Semantik: Zuerst wird der nach VAR stehende Typ ausgewerted. Dann wird eine zur FOR-VAR-Anweisung lokale Variable (Kontrollvaria- ble) des angegebenen Typs mit dem nach dem Doppelpunkt ste- henden Namen vereinbart. Danach wird die nach RANGE stehende Menge (siehe Kapitel 2.3.1.9) ausgewertet. Handelt es sich dabei um die leere Menge, so ist die FOR-VAR-Anweisung be- endet. Anschliessend erhaelt die Kontrollvariable den nied- rigsten (beziehungsweise wenn vor dem Symbol RANGE das Sym- bol REVERSE steht, den hoechsten) Wert der angegebenen Men- ge. Die zwischen DO und END stehenden Anweisungen werden durchgefuehrt. Wenn die Kontrollvariable noch nicht den hoechsten (bzw. niedrigsten) Wert der Menge erreicht hat, erhaelt sie den naechsthoeheren (bzw. naechstniedrigeren) Wert aus der Menge und die zwischen DO und END stehende Anweisungsfolge wird erneut durchgefuehrt. Dieser Vorgang wird so lange wiederholt, bis der hoechste (bzw. niedrigste) Wert der Menge erreicht ist. Wenn ein AND-Teil vorhanden ist, so wird die nach And stehende Bedingung nach jeder Zu- weisung an die Kontrollvariable ausgewerted. Ergibt diese Auswertung FALSE, so ist die FOR-Anweisung beendet. - 9 - Syntax: for_var_statement ::= 'FOR' 'VAR' type_expression ':' object_declaration [ 'REVERSE' ] 'RANGE' set_expression [ 'AND' expression ] 'DO' statement_sequence 'END' 'FOR' . Die Bedingung (expression) muss vom Typ Boolean sein. 2.2.6 IF-Anweisung z.B.: IF summe, >, >=, <, <= Funktionen: ORD Ordnungszahl ( Ergebnistyp: CARDINAL, ORD(FALSE)=0, ORD(TRUE)=1 ) BOOLEAN`VAL Umwandlung in den Typ BOOLEAN ( Argumenttyp: CARDINAL, BOOLEAN`VAL(0)=FALSE, BOOLEAN`VAL(1)=TRUE ) SUCC Nachfolger ( SUCC(FALSE)=TRUE, SUCC(TRUE) => FEHLER ) PRED Vorgaenger ( PRED(FALSE) => FEHLER ) PRED(TRUE)=FALSE ) STR Umwandeln in den Typ STRING ( Ergebnistyp: STRING, STR(FALSE)="FALSE", STR(TRUE)= "TRUE" ) BOOLEAN`VAL Umwandeln in den Typ BOOLEAN ( Argumenttyp: STRING, BOOLEAN`VAL("FALSE")=FALSE, BOOLEAN`VAL("TRUE") =TRUE, BOOLEAN`VAL(" TRUE ")=TRUE ) - 14 - Die Operatoren AND THEN und OR ELSE werten den zweiten Ope- randen nicht aus, wenn das Resultat schon nach Auswerten des ersten Operanden feststeht. Fuer den Typ BOOLEAN sind ausserdem die Konstanten BOOLEAN`FIRST und BOOLEAN`LAST mit den Werten FALSE bzw. TRUE definiert. 2.3.1.2 INTEGER Der Typ INTEGER enthaelt alle ganzen Zahlen. Die Literale des Typs INTEGER sind folgen von Ziffern. Literale: integer ::= digit { digit } . Monadische Praefixoperatoren: + Identitaet - Vorzeichenwechsel Monadische Postfixoperatoren: ! Faktorielle Infixoperatoren: + Addition - Subtraktion * Multiplikation DIV Ganzzahldivision ( Der rechte ) REM Rest bei der ( Operand ) Ganzzahldivision ( muss <> 0 ) MOD Modulo ( sein. ) ** Potenzieren ( Der rechte Operand muss >= 0 sein ) ! Binomialkoeffizient Vergleiche: =, <>, <, <=, >, >= Funktionen: ORD Identitaet INTEGER`VAL Identitaet SUCC Nachfolger ( SUCC(A) = A+1 ) PRED Vorgaenger ( PRED(A) = A-1 ) ABS Absolutwert ODD Ungerade ( Ergebnistyp: BOOLEAN ) RAT Umwandeln in den Typ RATIONAL ( Ergebnistyp: RATIONAL ) FLOAT Umwandeln in den Typ REAL ( Ergebnistyp: REAL ) STR Umwandeln in den Typ STRING ( Ergebnistyp: STRING ) INTEGER`VAL Umwandeln in den Typ INTEGER ( Argumenttyp: STRING, INTEGER`VAL(" -123 ")=-123 ) Fuer die Operation DIV gilt: A REM B = A - (A DIV B)*B Fuer alle A und B Fuer die Operation MOD gilt: A MOD B = A REM B Wenn A und B positiv oder A und B negativ sind. A MOD B = 0 Wenn A REM B = 0 ist. A MOD B = B + A REM B Wenn A REM B <> 0 ist und A und B verschiedene Vorzeichen haben. - 15 - Tabelle fuer das Verhalten von DIV, REM und MOD: A B A DIV B A REM B A MOD B 5 3 1 2 2 4 3 1 1 1 3 3 1 0 0 2 3 0 2 2 1 3 0 1 1 0 3 0 0 0 -1 3 0 -1 2 -2 3 0 -2 1 -3 3 -1 0 0 -4 3 -1 -1 2 -5 3 -1 -2 1 A B A DIV B A REM B A MOD B 5 -3 -1 2 -1 4 -3 -1 1 -2 3 -3 -1 0 0 2 -3 0 2 -1 1 -3 0 1 -2 0 -3 0 0 0 -1 -3 0 -1 -1 -2 -3 0 -2 -2 -3 -3 1 0 0 -4 -3 1 -1 -1 -5 -3 1 -2 -2 Bemerkung: In MASTER gibt es keine groesste ganze Zahl (MAXINT). Der Typ INTEGER ermoeglicht also das Rechnen mit beliebig grossen Zahlen. Die Rechenoperationen mit grossen Zahlen sind in "The Art of Computer Programming" [knuth 1981] beschrieben. 2.3.1.3 CARDINAL Der Typ CARDINAL enthaelt alle natuerlichen Zahlen (inclusi- ve 0). Fuer den Typ CARDINAL gelten dieselben Literale, Ope- ratoren, Vergleiche und Funktionen wie fuer den Typ INTEGER. Die Operatoren, Vergleiche und Funktionen erlauben auch das gemischte Verwenden von Argumenten beider Typen. Die Typen INTEGER und CARDINAL sind also voll kompatibel (siehe Kapitel 2.3.4) zueinander. Eine Operation oder Funk- tion ist fuer den Typ CARDINAL jedoch nur dann erlaubt, wenn ihr Ergebnis nicht negativ ist. Die Einhaltung dieser Be- stimmung wird vom Laufzeitsystem ueberprueft, eine Verlet- zung der Bestimmung fuehrt zu einem Fehler. - 16 - 2.3.1.4 RATIONAL Der Typ RATIONAL enthaelt die rationalen Zahlen. Die Litera- le des Typs RATIONAL sind endliche und periodische Dezimal- zahlen. (Die Periode einer periodischen Dezimalzahl wird durch ein Apostroph (') von der restlichen Zahl getrennt. Z.B.: 3.'3 oder 123.45'678) Literale: rational ::= integer '.' integer [ periode ] | integer '.' periode | integer periode [ '.' integer ] . periode ::= apostrophe integer . Monadische Praefixoperatoren: + Identitaet - Vorzeichenwechsel Infixoperatoren: + Addition - Subtraktion * Multiplikation / Division ** Potenzieren ( RATIONAL ** INTEGER ) Vergleiche: =, <>, <, <=, >, >= Funktionen: ABS Absolutwert TRUNC Abrunden Richtung 0 ( Ergebnistyp: INTEGER, TRUNC( 1.8)= 1, TRUNC( 1.0)= 1, TRUNC(-1.0)=-1, TRUNC(-1.8)=-1 ) ROUND Runden ( Ergebnistyp> INTEGER, ROUND(0.5)=1, ROUND(-0.5)=-1, ROUND(0.4)=0, ROUND(-0.4)=0 ) FLOAT Umwandeln in den Typ REAL ( Ergebnistyp: REAL ) STR Umwandeln in den Typ STRING ( Ergebnistyp: STRING ) RATIONAL`VAL Umwandeln in den Typ RATIONAL ( Argumenttyp: STRING ) Beim Rechnen mit RATIONAL-Zahlen werden alle Rechenoperatio- nen exact (ohne Rundungen) durchgefuehrt. Bemerkung: Die Darstellung des Typs RATIONAL erfolgt durch Zaehler (Typ: INTEGER) und Nenner (Typ: CARDINAL). 2.3.1.5 REAL Der Typ REAL enthaelt die reellen Zahlen. Die Literale des Typs REAL sind rationale Literale gefolgt von dem Zeichen ~. Literale: real ::= rational '~' . Monadische Praefixoperatoren: + Identitaet - Vorzeichenwechsel - 17 - Infixoperatoren: + Addition - Subtraktion * Multiplikation / Division ** Potenzieren ( REAL ** INTEGER ) ** Potenzieren ( REAL ** REAL, Der linke Operand muss >= 0 sein ) Vergleiche: =, <>, <, <=, >, >= Funktionen: ABS Absolutwert TRUNC Abrunden Richtung 0 ( Ergebnistyp: INTEGER, TRUNC( 1.8)= 1, TRUNC( 1.0)= 1, TRUNC(-1.0)=-1, TRUNC(-1.8)=-1 ) ROUND Runden ( Ergebnistyp> INTEGER, ROUND(0.5)=1, ROUND(-0.5)=-1, ROUND(0.4)=0, ROUND(-0.4)=0 ) RAT Umwandeln in den Typ RATIONAL ( Ergebnistyp: RATIONAL ) STR Umwandeln in den Typ STRING ( Ergebnistyp: STRING ) REAL`VAL Umwandeln in den Typ REAL ( Argumenttyp: STRING ) SQRT Quadratwurzel SIN Sinus COS Cosinus ARCTAN Arcus Tangens EXP Exponentialfunktion LN Logarithmus Naturalis Beim Rechnen mit REAL-Zahlen werden alle Rechenoperationen gerundet durchgefuehrt. 2.3.1.6 CHAR Der Typ CHAR enthaelt die Zeichen des ASCII-Zeichensatzes. Die Literale des Typs CHAR sind in Hochkomma eingeschlosse- ne ASCII-Zeichen. Zum Beispiel: ' ' '"' ''' 'A' '^' 'z' '`' Kontrollzeichen koennen auf diese Art nicht angeschrieben werden. (Die vordefinierten Konstanten fuer Kontrollzeichen finden sich im Kapitel 3.5) Literale: char ::= apostrophe printable_character apostrophe . printable_character ::= not_space | space_character . Vergleiche: =, <>, <, <=, >, >= Funktionen: ORD Ordnungszahl ( Ergebnistyp: CARDINAL ) CHAR`VAL Umwandeln in den Typ CHAR ( Argumenttyp: CARDINAL ) SUCC Nachfolger ( SUCC(A)=CHAR`VAL(SUCC(ORD(A))) ) PRED Vorgaenger ( PRED(A)=CHAR`VAL(PRED(ORD(A))) ) - 18 - STR Umwandeln in den Typ STRING ( Ergebnistype: STRING ) UPPER Grossbuchstabe ( UPPER('A') = 'A' ) ( UPPER('z') = 'Z' ) LOWER Kleinbuchstabe ( LOWER('A') = 'a' ) ( LOWER('z') = 'z' ) Fuer den Typ CHAR sind ausserdem die Konstanten CHAR`FIRST und CHAR`LAST mit den Werten CHAR`VAL(0) bzw. CHAR`VAL(127) definiert. 2.3.1.7 STRING Der Typ STRING enthaelt aus ASCII-Zeichen zusammengesetzte Zeichenketten (inclusive der leeren Zeichenkette). Die Lite- rale des Typs STRING sind in Anfuehrungszeichen eingeschlos- sene Folgen von ASCII-Zeichen. Zum Beispiel: "" " " """" "'" "Kette" "CAN""T !" Zeichenkettenliterale duerfen keine Kontrollzeichen enthal- ten. Anfuehrungszeichen innerhalb von Zeichenketten werden durch zwei nebeneinanderstehende Anfuehrungszeichen ( "" ) dargestellt. Literale: string ::= quotation { not_quotation | quotation quotation } quotation . Infixoperatoren: & Stringverkettung ( "Alles " & "OK" = "Alles OK" ) TIMES Stringvervielfaeltigung ( Linker Operand: CARDINAL, 3 TIMES "LA" = "LALALA" ) : Formatieren ( Rechter Operand: INTEGER, "HALLO" : 8 = " HALLO", "HALLO" : 3 = "HALLO", "HALLO" :-8 = "HALLO ", "HALLO" : 0 = "HALLO" ) : Formatieren ( Rechter Operand: INTEGER, Linker Operand: BOOLEAN, INTEGER, RATIONAL, REAL, CHAR oder ENUMERATION, A : ZAHL = STR(A) : ZAHL ) Indizierung: [ index ] Zugriff auf ein Zeichen ( Indextyp: CARDINAL, Ergebnistyp: CHAR, A[1]=Erstes Zeichen ) [ index .. index ] Zugriff auf einen Teilstring ( Indextyp: CARDINAL ) [ index .. ] Zugriff auf einen Teilstring ab dem angegebenen Index ( Indextyp: CARDINAL ) - 19 - [ .. index ] Zugriff auf einen Teilstring bis zum angegebenen INDEX ( Indextyp: CARDINAL ) Vergleiche: =, <>, <, <=, >, >= Funktionen: LENGTH Laenge der Zeichenkette ( Ergebnistyp: CARDINAL, LENGTH("") = 0 ) POS(A,B) Position des STRINGs B im STRING A ( Ergebnistyp: CARDINAL, POS("ABCDE ABCDE","BC")=2, POS("XYZXYZ","ZYX")=0, POS("123456789","")=0 ) DELETE(A,B) Liefert den STRING A ohne alle Vorkommnisse des STRINGs B ( DELETE("EINE ZEILE","E")= "IN ZIL" ) REPLACE(A,B,C) Liefert den STRING A in dem alle Vorkommnisse des STRINGS B durch den STRING C ersetzt sind. ( REPLACE("ABRAHAM A SANTA CLARA", "A","E") = "EBREHEM E SENTE CLERE" ) UPPER Grossbuchstaben ( UPPER("Gross")="GROSS" ) LOWER Kleinbuchstaben ( LOWER("Klein")="klein" ) Die Indizierung kann zu beiden Seiten der Zuweisung verwen- det werden. Zum Beispiel: LINE := "Dieser Satz ist richtig"; LINE := LINE [.. 15]; (* LINE = "Dieser Satz ist" *) LINE := LINE [8 ..]; (* LINE = "Satz ist" *) LINE := LINE [2 .. 3]; (* LINE = "at" *) ZEICHEN := LINE [2]; (* ZEICHEN = 't' *) ZEICHEN := LINE [5]; (* FEHLER: 5 > length[LINE] *) LINE := 2 times LINE; (* LINE = "atat" *) LINE := LINE [2 .. 10]; (* LINE = "tat" *) LINE := LINE [.. 5]; (* LINE = "tat" *) LINE := LINE [5 ..]; (* LINE = "" *) LINE := "Dieser Satz ist falsch"; LINE [3 .. 20] := ""; (* LINE = "Dich" *) LINE [2 .. 2] := "ur"; (* LINE = "Durch" *) LINE [7 ..] := "Uebung"; (* LINE = "Durch Uebung" *) LINE [.. 5] := "mit"; (* LINE = "mit Uebung" *) LINE [1] := 'M'; (* LINE = "Mit Uebung" *) LINE [5 .. 3] := "viel "; (* LINE = "Mit viel Uebung" *) LINE [20] := '!'; (* FEHLER: 20 > length(LINE) *) 2.3.1.8 ENUMERATION Mit ENUMERATION { identifier_declaration ',' } END ENUMERATION - 20 - wird ein Aufzaehlungstyp beschrieben. Die Literale dieses Aufzaehlungstyps werden bei der Deklaration aufgezaehlt. Literale: enumeration ::= identifier . Vergleiche: =, <>, <, <=, >, >= Funktionen: ORD Ordnungszahl ( Ergebnistyp: CARDINAL, ORD(erstes_literal)=0 ) typ`VAL Umwandeln in den Aufzaehlungstyp ( Argumenttyp: CARDINAL, typ`VAL(0)=erstes_literal ) SUCC Nachfolger ( SUCC(A)=typ`VAL(SUCC(ORD(A))) ) PRED Vorgaenger ( PRED(A)=typ`VAL(PRED(ORD(A))) ) STR Umwandeln in den Typ STRING ( Ergebnistyp: STRING ) typ`VAL Umwandeln in den Aufzaehlungstyp ( Argumenttyp: STRING ) Ausserdem sind fuer einen Aufzaehlungstyp die Konstanten typ`FIRST und typ`LAST definiert, die das Erste und das Letzte Literal des Aufzaehlungstyps als Wert haben. 2.3.1.9 SET Der Typ SET OF komponententyp beschreibt eine Menge von Ele- menten des Typs komponententyp. (Analog den SETs aus PASCAL [Jensen/Wirth 1978] und MODULA [Wirth 1982]). Konstante: typ`{} Leere Menge Infixoperatoren: + Vereinigung * Durchschitt - Mengendifferenz / Symmetrische Differenz IN Element ( Linker Operand: komponententyp, Ergebnistyp: BOOLEAN ) Vergleiche: =, <>, <, <=, >, >= Funktionen: CARD Kardinalitaet einer Menge ( Ergebnistyp: CARDINAL, CARD(typ`{}) = 0 ) FIRST_ELEMENT ( Ergebnistyp: komponententyp, Liefert jenes Element aus der Menge fuer das gilt: Element <= X fuer alle X aus der Menge. FIRST_ELEMENT(typ`{}) => FEHLER ) LAST_ELEMENT ( Ergebnistyp: komponententyp, Liefert jenes Element aus der Menge fuer das gilt: Element >= X fuer alle X aus der Menge. FIRST_ELEMENT(typ`{}) => FEHLER ) - 21 - NEXT_ELEMENT(M,E) ( Ergebnistyp: komponententyp, Liefert jenes Element aus der Menge M fuer das gilt: Element > E und es gibt kein X aus der Menge M fuer das gilt: Element > X > E Existiert kein solches Element ist das Ergebnis gleich: FIRST_ELEMENT(M) ) PREV_ELEMENT(M,E) ( Ergebnistyp: komponententyp, Liefert jenes Element aus der Menge M fuer das gilt: Element < E und es gibt kein X aus der Menge M fuer das gilt: Element < X < E Existiert kein solches Element ist das Ergebnis gleich: LAST_ELEMENT(M) ) Bemerkung: Die FOR-Anweisung (siehe Kapitel 2.2.4) und die FOR-VAR-Anweisung (siehe Kapitel 2.2.5) werden unter Ver- wendung von FIRST_ELEMENT, LAST_ELEMENT, NEXT_ELEMENT und PREV_ELEMENT implementiert. Fuer die Typen SET OF BOOLEAN, SET OF INTEGER, SET OF CARDINAL und SET OF CHAR koennen SET-Generatoren der Form: set_generator ::= '{' member [ ',' member ] '}' . member ::= expression [ '..' expression ] . verwendet werden (Wobei die Expressions vom Typ BOOLEAN, INTEGER, CARDINAL bzw. CHAR sein muessen). Fuer den Typ SET OF RATIONAL kann ein SET-Generator der Form rat_set_generator ::= '{' rat_member [ ',' rat_member ] '}' . rat_member ::= expression [ '..' expression [ 'DELTA' expression ] ] . verwendet werden (Wobei die Expressions vom Typ RATIONAL sein muessen). Fuer den Typ SET OF REAL kann ein Generator der Form real_set_generator ::= '{' real_member [ ',' real_member ] '}' . real_member ::= expression [ '..' expression [ 'DIGITS' expression ] ] . verwendet werden (Wobei die Expressions vom Typ REAL sein muessen). Fuer Aufzaehlungstypen gilt dieselbe Art von Gene- rator wie set-generator. Der set_generator fuer die Menge - 22 - eines Aufzaehlungstyps kann erst verwendet werden, wenn mit SET OF Aufzaehlungstyp der entsprechende SET-Typ vereinbart wurde. 2.3.1.10 POINTER Mit POINTER TO elementtyp wird ein Zeigertyp beschrieben. Konstante: typ`NIL Zeiger zeigt auf kein Element. Dereferenzieren: ^ Zugriff auf die Komponente, auf die der Zeiger weist. ( Ergebnistyp: elementtyp ) Vergleiche: =, <> Prozeduren: NEW Generieren eines neuen Elementes DISPOSE Zerstoeren eines Elementes Das Dereferenzieren kann zu beiden Seiten der Zuweisung ver- wendet werden. Zum Beispiel: zeig1^.next := zeig2^.follow; 2.3.1.11 ARRAY Mit ARRAY indextyp OF komponententyp wird ein Datenfeld durch Angabe des Komponententyps und des Indextyps beschrie- ben. Indizierung: [ index ] Zugriff auf eine Komponente ( Ergebnistyp: komponententyp, Der Index muss den Typ Indextyp haben ) Vergleiche: =, <>, <, <=, >, >= Bei der Anwendung des Indizierungsoperators auf ein ARRAY wird die dem Index entsprechende Komponente geliefert. So liefert zum Beispiel: FELD [ 5 ] die Komponente mit Index fuenf des FELDes. Die Zuweisung FELD [ 3 ] := 1; weist der Komponente mit Index drei des FELDes den Wert 1 zu. - 23 - 2.3.1.12 RECORD Mit RECORD { type_expression ':' component_declaration_list } END RECORD component_declaration_list ::= object_declaration { ',' object_declaration } ';' . wird ein Datenverbund mit den angegebenen Komponenten be- schrieben. Ein solcher Datenverbund entspricht dem RECORD von PASCAL [Jensen/Wirth 1978]. Selektierung: . name Zugriff auf die Komponente des angegebenen Namens ( Ergebnistyp: Typ der entsprechenden Komponente ) Vergleiche: =, <>, <, <=, >, >= Die Selektierung darf zu beiden Seiten der Zuweisung verwen- det werden. Zum Beispiel: vektor1.x := vektor2.y; 2.3.1.13 UNION Mit UNION tagfield_type ':' tagfield_name 'OF' { 'WHEN' set_expression ':' { type_expression ':' component_declaration_list } } { 'OTHERWISE' ':' { type_expression ':' component_declaration_list } } END UNION component_declaration_list ::= object_declaration { ',' object_declaration } ';' . Wird ein varianter Datenverbund beschrieben. Die set_expressions muessen vom Typ SET OF tagfield_type sein und die Durchschnittsmenge aller set_expressions muss leer sein. Selektierung: . name Zugriff auf die Komponente des angegebenen Namens ( Ergebnistyp: Typ der entsprechenden Komponente ) Vergleiche: =, <>, <, <=, >, >= Es darf immer nur auf jene Komponenten des varianten Daten- verbundes zugegriffen werden, deren set_expression den mo- mentanen Wert von tagfield_name enthaelt. Wenn der momentane Werd von tagfield_name in keiner der set_expressions enthal- ten ist, dann darf auf die Komponenten des OTHERWISE-Teiles zugegriffen werden. - 24 - 2.3.1.14 TYPE Der Typ TYPE enthaelt alle Typen. Werte des Typs TYPE sind zum Beispiel: BOOLEAN, INTEGER, CARDINAL, RATIONAL, REAL, CHAR, STRING, ENUMERATION ... END ENUMERATION, SET OF ..., POINTER TO ..., ARRAY ... OF ..., RECORD ... END RECORD, TYPE, PROC, PROC RETURN ..., usw. und alle durch den Anwender definierten Typen. Vergleiche: <|, |>, <#, #>, <||>, <#|>, <|#>, <##> (siehe Kapitel 2.3.3) 2.3.1.15 PROC Der Typ PROC enthaelt alle Prozeduren. Werte des Typs PROC sind zum Beispiel: ... := ..., WHILE ... END WHILE, REPEAT ... UNTIL ..., FOR ... END FOR, FOR VAR ... END FOR, IF ... END IF, CASE ... END CASE, BEGIN ... END, LOCAL ... END, usw. und alle durch den Anwender definierten Prozeduren. 2.3.1.16 PROC RETURN xtyp Der Typ PROC RETURN xtyp enthaelt alle Funktionen die ein Ergebnis des Typs xtyp haben. Werte des Typs PROC RETURN xtyp sind zum Beispiel: ORD, PRED, SUCC, STR, VAL, RAT, FLOAT, TRUNC, ROUND, ODD, ABS, LENGTH, POS, DELETE, REPLACE, UPPER, LOWER, +, -, *, /, DIV, REM, MOD, &, TIMES, usw. und alle durch den Anwender definierten Funktionen. - 25 - 2.3.2 Anwenderdefinierte Typen Der Anwender hat die Moeglichkeit, sich mit Hilfe der LOCAL- Anweisung (siehe Kapitel 2.2.9) eigene Typen zu definieren. z.B.: LOCAL CONST TYPE ZEICHEN = CHAR, GANZZAHL = INTEGER; BEGIN . . . END; Wie man an diesem Beispiel sehen kann, werden neue Typen als Konstante des Typs TYPE deklariert und mit einem Typausdruck initialisiert. Theoretisch waere auch die Deklaration von variablen Typen moeglich: LOCAL VAR TYPE: TYPEVARIABLE := INTEGER; TYPEVARIABLE: VARI; Das bedeutet jedoch nicht, dass die Variable VARI Werte je- des beliebigen Typs annehmen kann. Vielmehr erhaelt die Va- riable VARI den Typ, den die Typvariable TYPEVARIABLE zum Zeitpunkt der Vereinbarung von VARI hat. In diesem Fall erhaelt VARI also den Typ INTEGER. - 26 - 2.3.3 Kompatibilitaet von Typen Da der Begriff der Kompatibilitaet wichtig ist, soll nach- folgend genauer darauf eingegangen werden. Damit eine Parameteruebergabe legal ist, muss unter anderem der Typ des formalen Parameters kompatibel ( |> ) zu dem des aktuellen Parameters sein. Fuer die Parameteruebergabe muss also gelten: typ_des_formalen_parameters |> typ_des_aktuellen_parameters beziehungsweise: typ_des_aktuellen_parameters |> typ_des_formalen_parameters Wenn sowohl TYP1 |> TYP2 (sprich: TYP1 abwaertskompatibel zu TYP2) als auch TYP1 <| TYP2 (sprich: TYP1 aufwaertskompatibel zu TYP2) gilt, kann man auch schreiben: TYP1 <||> TYP2 (sprich: TYP1 voll kompatibel zu TYP2) Weiters ist definiert: nicht abwaertskompatibel: TYP1 #> TYP2 = NOT (TYP1 |> TYP2) nicht aufwaertskompatibel: TYP1 <# TYP2 = NOT (TYP1 <| TYP2) nicht kompatibel: TYP1 <##> TYP2 = (TYP1 <# TYP2) AND (TYP1 #> TYP2) strikt abwaertskompatibel: TYP1 <#|> TYP2 = (TYP1 <# TYP2) AND (TYP1 |> TYP2) strikt aufwaertskompatibel: TYP1 <|#> TYP2 = (TYP1 <| TYP2) AND (TYP1 #> TYP2) Rechenregeln fuer die Kompatibilitaet: A|>B & B|>C --> A|>C A<|B & B<|C --> A<|C A|>B & B#>C --> A#>C A<|B & B<#C --> A<#C A#>B & B|>C --> A#>C A<#B & B<|C --> A<#C A#>B & B#>C --> A#>C A<#B & B<#C --> A<#C Fuer die zusammengesetzten Kompatibilitaeten gelten diese Rechenregeln sinngemaess. - 27 - In herkoemmlichen Programmiersprachen, die ein Typkonzept besitzen, wird meist zwischen der Zuseisungskompatibilitaet und der Kompatibilitaet bei der Parameteruebergabe unter- schieden. In MASTER existiert diese Unterscheidung nicht. (Zwei Typen sind genau dann zuweisungskompatibel, wenn sie voll kompatibel sind) Die Kompatibilitaet bei der Parameteruebergabe wird ueblicherweise nach zwei Schemata unterschieden: - Bei der Strukturkompatibilitaet sind zwei Typen dann kompatibel, wenn sie durch dieselbe Struktur beschrie- ben werden. Die Programmiersprache PASCAL [Jensen/Wirth 1978] verwendet je nach Implementierung Struktur- kompatibilitaet, Namenskompatibilitaet, oder eine Kom- bination dieser beiden Prinzipien. - Bei der Namenskompatibilitaet sind zwei Typen dann kom- patibel, wenn sie durch denselben Namen bezeichnet wer- den. In MODULE [Wirth 1982] und ADA [ANSI 1983] wird eine Art von Namenskompatibilitaet verwendet. In MASTER existiert kein starres Schema, durch das die Kompatibilitaet zweier Typen automatisch festgelegt wird. Der Anwender hat vielmehr selbst die Moeglichkeit, bei der Deklaration eines neuen Typs festzulegen, ob und wie kompa- tibel der neue Typ zu den bisher gueltigen Typen ist. In den nachfolgenden Kapiteln wird beschrieben, wie der Anwender verschiedene kompatible Typen vereinbaren kann. 2.3.4 Deklaration voll kompatibler Typen Bei der Deklaration eines Typs wird festgelegt, welche Kompatibilitaeten zwischen dem neuen Typ und den bisherigen Typen gelten. So werden z.B. mit LOCAL CONST TYPE: RIBISELN = INTEGER, JOHANNISBEEREN = INTEGER; die Typen RIBISELN und JOHANNISBEEREN vereinbart, fuer die gilt: RIBISELN <||> INTEGER JOHANNISBEEREN <||> INTEGER RIBISELN <||> JOHANNISBEEREN Die Typen RIBISELN und JOHANNISBEEREN sind also voll kompa- tibel zum Typ INTEGER. Das bedeutet, dass alle Funktionen und Prozeduren, die fuer den Typ INTEGER definiert sind, auch fuer die Typen RIBISELN und JOHANNISBEEREN gelten. Wei- ters gelten auch die fuer die Typen RIBISELN und JOHANNISBEEREN definierten Prozeduren und Funktionen fuer den Typ INTEGER. Es koennte somit an jeder Stelle, an der INTEGER steht, auch RIBISELN oder JOHANNISBEEREN stehen. - 28 - 2.3.5 Deklaration aufwaertskompatibler Typen Es ist nicht immer wuenschenswert, bei der Deklaration einen voll kompatiblen Typ zu erhalten. Zu diesem Zweck gibt es in MASTER die Moeglichkeit, Untertypen (nicht zu verwech- seln mit Teilbereichstypen) zu vereinbaren. Wenn man z.B. vereinbart: LOCAL CONST TYPE: AEPFEL = SUBTYPE INTEGER, BIRNEN = SUBTYPE INTEGER; dann gilt: AEPFEL <|#> INTEGER BIRNEN <|#> INTEGER AEPFEL <##> BIRNEN Das bedeutet, dass alle Funktionen und Prozeduren, die fuer den Typ INTEGER definiert sind, auch fuer die Typen AEPFEL und BIRNEN gelten, unabhaengig davon, ob sie vor oder nach der Deklaration der Typen AEPFEL und BIRNEN vereinbart worden sind. Die fuer die Typen AEPFEL und BIRNEN definier- ten Prozeduren und Funktionen gelten jedoch nicht fuer den Typ INTEGER. Ausserdem gelten fuer AEPFEL vereinbarte Proze- duren und funktionen nicht fuer BIRNEN und umgekehrt. Wenn eine Variable APFEL vom Typ AEPFEL, eine Variable BIRNE vom Typ BIRNEN und eine Funktion SAFT mit einem Argument vom Typ AEPFEL vereinbart wurden dann ist: APFEL + APFEL erlaubt (+ ist fuer INTEGER definiert) 3 * BIRNE erlaubt (* ist fuer INTEGER definiert) SAFT(APFEL) erlaubt (SAFT ist fuer AEPFEL definiert) SAFT(BIRNE) verboten (Funktionen fuer AEPFEL gelten SAFT(5) verboten nicht fuer BIRNEN und INTEGER) APFEL + BIRNE erlaubt (+ ist fuer INTEGER definiert) Die SUBTYPEs von MASTER erlauben also im Unterschied zu den derived types von ADA [ANSI 1983, Nagle 1982] die kominierte Verwendung von Untertypen und ihrer Vatertypen in Aus- druecken. 2.3.6 Deklaration nicht kompatibler Typen Wenn man vermeiden moechte, dass ein neu vereinbarter Typ alle Funktionen und Prozeduren seines Vatertyps uebernimmt, muss man NEWTYPEs benutzen. Ein mit NEWTYP vereinbarter Typ uebernimmt nur die Werte seines Vatertyps. - 29 - Nach der Vereinbarung: LOCAL CONST TYPE: PFIRSICHE = NEWTYPE INTEGER, ORANGEN = NEWTYPE INTEGER; gilt: PFIRSICHE <##> INTEGER ORANGEN <##> INTEGER PFIRSICHE <##> ORANGEN Das bedeutet, dass keine der Funktionen und Prozeduren, die fuer den Typ INTEGER definiert sind, fuer die Typen PFIRSICHE und ORANGEN gelten. Die fuer die Typen PFIRSICHE und ORANGEN definierten Prozeduren und Funktionen gelten auch nicht fuer den Typ INTEGER. Dadurch, dass keinerlei Funktionen und Prozeduren ueber- nommen werden ist es moeglich die Funktionen und Prozeduren den Physikalischen Begebenheiten anzupassen: METER + METER ===> METER METER * METER ===> QUADRATMETER INTEGER * KILOGRAMM ===> KILOGRAMM METER / SEKUNDE ===> METER_PRO_SEKUNDE Mit den drei angegebenen Arten zur Deklaration von Typen lassen sich alle gewuenschten Kompatibilitaeten zwischen altem und neuem Typ erreichen. Die drei letzten Beispiele ergeben folgende Typhierarchie: /------- INTEGER --------\ / / | | \ \ <||> / | | \ <##> / <||> | | <##> \ RIBISELN / <|#> <|#> \ ORANGEN / | | \ JOHANNIS- | | PFIRSICHE BEEREN | | AEPFEL BIRNEN Diese Hiererchie laesst sich natuerlich beliebig ausbauen (z.B. mit KERNLOSEN_ORANGEN als SUBTYPE von ORANGEN). Ausser den in der obigen Zeichnung angegebenen Beziehungen gelten noch die folgenden: RIBISELN <||> JOHANNISBEEREN RIBISELN <|#> AEPFEL RIBISELN <|#> BIRNEN JOHANNISBEEREN <|#> AEPFEL JOHANNISBEEREN <|#> BIRNEN OBSTTYP_A <||> OBSTTYP_A OBSTTYP_A <##> OBSTTYP_B sonst - 30 - 2.3.7 Deklaration von Strukturtypen Fuer die mit TYPE vereinbarten Typen gilt, dass jeder Typ mit sich selbst kompatibel ist. Daher gilt z.B.: INTEGER <||> INTEGER, BOOLEAN <||> BOOLEAN, AEPFEL <||> AEPFEL, usw. Es gibt aber auch Typen, fuer die nicht TYPX <||> TYPX gel- ten soll. So ist es z.B. wuenschenswert, dass ARRAY BOOLEAN OF INTEGER <##> ARRAY BOOLEAN OF INTEGER gilt. (In PASCAL [Jensen/Wirth 1978] und MODULA [Wirth 1982] ist das der Fall.) Nach der Vereinbarung: LOCAL VAR ARRAY BOOLEAN OF INTEGER: A, B; ARRAY BOOLEAN OF INTEGER: C; ist A:=B erlaubt und A:=C verboten. Das wird durch folgende Vereinbarung von ARRAY erreicht: LOCAL CONST PROC RETURN NEWTYPE: ARRAY (CONST TYPE: INDEXTYPE; "OF"; CONST TYPE: COMPONENTTYPE) = ...; In MASTER ist der Typ ARRAY in der obigen Weise vordefi- niert. Ebenso sollen alle RECORD-Typen nicht kompatibel zueinander sein, selbst wenn sie gleiche Struktur und gleiche Komponen- tennamen haben. Nachfolgend eine Tabelle, die die Deklarationsmoeglichkeiten fuer Typen und die daraus entstehenden Kompatibilitaeten zu- sammenfasst: TYPE: A = B; A <||> A A <||> B TYPE: C = SUBTYPE D; C <||> C C <|#> D TYPE: E = NEWTYPE F; E <||> E E <##> F SUBTYPE: G = H; G <||> G G <|#> H SUBTYPE: I = SUBTYPE J; I <||> I I <|#> J SUBTYPE: K = NEWTYPE L; K <||> K K <##> L NEWTYPE: M = N; M <##> M M <##> N NEWTYPE: O = SUBTYPE P; O <##> O O <##> P NEWTYPE: Q = NEWTYPE R; Q <##> Q Q <##> R - 31 - 2.3.8 Teilbereichstypen Es gibt haeufig Variable, die nicht alle Werte eines Typs zugewiesen bekommen koennen (z.B.: Eine Variable fuer den Monat eines Datums). Diese Variable erhalten normalerweise nur Werte aus einem Teilbereich eines Typs. Falls eine sol- che Variable dennoch einen anderen Wert zugewiesen bekommt, muss ein Fehler im Programm vorliegen. Um Teilbereichstypen beschreiben zu koennen, gibt es in MASTER die Moeglichkeit, Teilbereiche eines Typs zu definieren. Die Definition eines Teilbereichstyps veraendert die Kompatibilitaet zu anderen Typen nicht. Ein Teilbereichstyp hat die selbe Kompatibilitaet zu anderen Typen wie der Typ, von dem er abstammt. 2.3.8.1 Subrange-Typen Der Subrange-Typ CHAR`RANGE{'A'..'Z'} enthaelt alle Gross- buchstaben. Der Subrange-Typ INTEGER`RANGE{2,3,5,7} enthaelt alle Primzahlen kleiner als 10. Ein Subrange-Typ wird also durch TYP`RANGE MENGE gebildet. Er enthaelt die in der MENGE enthaltenen Werte des Typs TYP. Die Elemente von MENGE muessen in TYP enthalten sein. Der Typ CARDINAL ist definiert als ein Subrange-Typ des Typs INTEGER, den nur die nichtnegativen Zahlen enthaelt. 2.3.8.2 Array-Typen Der Typ ARRAY CHAR`RANGE{'A'..'Z'} OF REAL ist ein Teil- bereichstyp des Typs ARRAY CHAR OF REAL. Da jedoch ARRAY- Typen nicht kompatibel zu sich selbst sind (siehe Kapitel 2.3.7), gilt: ARRAY CHAR`RANGE{'A'..'Z'} OF REAL <##> ARRAY CHAR OF REAL 2.3.9 Explizite Typconversion Fuer alle mit TYPE: ABC = SUBTYPE XYZ; und mit TYPE: BCD = NEWTYPE UVW; vereinbarten Typen sind folgende Konvertierungsfunktionen vordefiniert: OLD(A) Rueckkonvertierung ( Argumenttyp: ABC oder BCD, Ergebnistyp: XYZ bzw. UVW ) typ`VAL(Z) Vorkonvertierung ( typ ist der Typ, in den konvertiert werden soll, ( Argumenttyp: XYZ oder UVW, Ergebnistyp: ABC bzw. BCD ) - 32 - 2.4 PROZEDUREN UND ANWEISUNGEN ============================== Prozeduren ermoeglichen es, haeufig vorkommende und/oder lo- gisch zusammengehoerende Programmteile zusammenzufassen. In MASTER gibt es jedoch nicht die in anderen Programmiersprachen uebliche Unterscheidung zwischen Prozeduren und Anweisungen. Mit Anweisung werden hier die vordefinierten Prozeduren be- zeichnet, waehrend der Name Prozedur fuer Anweisungen und an- wenderdefinierte Prozeduren verwendet wird. Die Kommunikation einer Prozedur mit der Umgebung erfolgt mittels globalen Varia- blen und Parametern. Die ueblichen Parameterarten, die den Zweck haben, Datenwerte auszutauschen, werden hier normale Pa- rameter genannt. Darueber hinaus gibt es in MASTER weitere Arten von Parametern, die die Erweiterung der Programmierspra- che um neue 'Anweisungen' erlauben. Die verschiedenen Parameter werden in den folgenden Unterkapiteln der Reihe nach erklaert. parameter_list ::= '(' parameter { ';' parameter } ')' . parameter ::= normal_parameter | help_parameter | symbol_parameter | parameter_array | parameter_case | local_parameter | initial_parameter . 2.4.1 Normale Parameter Die normalen Parameter teilen sich in CONST-, IN-, VAR-, OUT- und RETURN-Parameter. normal_parameter ::= const_parameter | in_parameter | var_parameter | out_parameter | return_parameter . 2.4.1.1 CONST-Parameter Die Prozedur PRINT druckt, wenn sie mit PRINT N aufgerufen wird, die uebergebene Zahl N aus: LOCAL CONST PROC: PRINT (CONST CARDINAL: L) = WRITE(L); Bemerkung: Beim Aufruf der Prozedur PRINT muss man den aktu- ellen Parameter nicht in Klammern einschliessen. Wenn der aktuelle Parameter in Klammern eingeschlossen werden soll, kann man das durch Verwendung von Symbol-Parametern (siehe Kapilel 2.4.2) erreichen. Semantik: Beim Aufruf der Prozedur uebernimmt der CONST-Parameter den Wert des aktuellen Parameters. Auf den uebergebenen Wert kann innerhalb der Prozedur nur ein Lesezugriff stattfin- den. Man kann einen CONST-Parameter mit einem Defaultwert - 33 - versehen. Der Defaultwert tritt in Kraft, wenn der Parameter in der aktuellen Parameterliste ausgelassen wird. Syntax: const_parameter ::= 'CONST' type_expression ':' identifier_declaration [ ':= expression ] { ',' identifier_declaration [ ':= expression ] } . Fuer CONST-Parameter sind, wie auf mit CONST vereinbarte Objekte (Konstante), nur Lesezugriffe erlaubt. Im folgenden werden Objekte, auf die nur Lesezugriffe erlaubt sind, als Objekte mit Zugriffsrecht CONST bezeichnen. CONST-Parameter und mit CONST vereinbarte Objekte haben also das Zugriffs- recht CONST. 2.4.1.2 IN-Parameter Die Prozedur WRITENUMBER schreibt eine Zahl mit fuehrenden Nullen aus: LOCAL CONST PROC: WRITENUMBER (IN CARDINAL`RANGE{0..9999}: N) = FOR VAR CARDINAL: I REVERSE RANGE {0..3} DO WRITE(N DIV 10**I); N:=N MOD 10*I; END FOR; Semantik: Ein IN-Parameter uebernimmt den Wert des aktuellen Parame- ters beim Aufruf der Prozedur. Der uebergebene Wert kann in- nerhalb der Prozedur gelesen und veraendert werden. Eine Aenderung der formalen Parameters hat jedoch keine Wirkung auf den aktuellen Parameter. Man kann einen IN-Parameter mit einem Defaultwert versehen. Der Defaultwert tritt in Kraft, wenn der Parameter in der aktuellen Parameterliste ausgelas- sen wird. Syntax: in_parameter ::= 'IN' type-expression ':' identifier_declaration [ ':=' expression ] { ',' identifier_declaration [ ':=' expression ] } . Fuer IN-Parameter sind Lesezugriffe und Schreibzugriffe er- laubt. Im folgenden werden Objekte, auf die Lese- und Schreibzugriffe erlaubt sind, als Objekte mit Zugriffsrecht VAR bezeichnet. - 34 - 2.4.1.3 VAR-Parameter Die nachfolgend vereinbarte Prozedure INCR erhoeht bei dem Aufruf INCR K den Wert der Variablen K um eins: LOCAL CONST PROC: INCR (VAR INTEGER: I) = I:=I+1; Semantik: Beim Aufruf der Prozedur uebernimmt der VAR-Parameter den Wert des aktuellen Parameters. Der uebergebene Wert kann innerhalb der Prozedur gelesen und veraendert werden. Am Ende der Prozedur wird der Wert des formalen Parameters an den aktuellen Parameter zurueckgegeben. Der VAR-Parameter von MASTER unterscheidet sich also vom VAR-Parameter von PASCAL [Jensen/Wirth 1978]. Syntax: var_parameter ::= 'VAR' type_expression ':' identifier_declaration { ',' identifier_declaration } . Fuer VAR-Parameter sind, wie auf mit VAR vereinbarte Objekte (Variable), Lesezugriffe und Schreibzugriffe erlaubt. VAR- Parameter und mit VAR vereinbarte Objekte haben also das Zu- griffsrecht VAR. 2.4.1.4 OUT-Parameter Die Prozedur SUCHMAX sucht den Maximalwert des Feldes F: LOCAL CONST PROC: SUCHMAX (OUT CARDINAL: MAX) = BEGIN MAX:=0; FOR VAR CARDINAL: I RANGE {1..10} DO IF F[I]>MAX THEN MAX:=F[I]; END IF; END FOR; END; Semantik: Nach dem Aufruf der Prozedur hat der OUT-Parameter entweder keinen Wert, oder wenn er in der formalen Parameterliste in- itialisiert wurde, den Initialisierungswert. Der Wert des formalen Parameters kann veraendert und, wenn er nicht unde- finiert ist, gelesen werden. Erst am Ende der Prozedur wird der Wert des formalen Parameters an den aktuellen Parameter zurueckgegeben. - 35 - Syntax: out_parameter ::= 'OUT' type_expression ':' identifier_declaration [ ':=' expression ] { ',' identifier_declaration [ ':=' expression ] } . Fuer OUT-Parameter sind Lese- und Schreibzugriffe erlaubt. OUT-Parameter haben also das Zugriffsrecht VAR. 2.4.1.5 RETURN-Parameter Die Prozedur SET_TO_PI weist bei dem Aufruf SET_TO_PI X der Variablen X den Wert PI zu: LOCAL CONST PROC: SET_TO_PI (RETURN REAL: A) = A:=3.1415926535897932384626433832795028841972~; Semantik: Nach dem Aufruf der Prozedur hat ein RETURN-Parameter entwe- der keinen Wert, oder wenn er in der formalen Parameterliste initialisiert wurde, den Initialisierungswert. Der Wert des formalen Parameters kann veraendert, jedoch nicht gelesen werden. Am Ende der Prozedur wird der Wert des formalen Pa- rameters dem aktuellen Parameter zugewiesen. Syntax: return_parameter ::= 'RETURN' type_expression ':' identifier_declaration [ ':=' expression ] { ',' identifier_declaration [ ':=' expression ] } . Fuer RETURN-Parameter sind, wie auf mit RETURN vereinbarte Objekte (Pseudovariable), nur Schreibzugriffe erlaubt. Im folgenden werden Objekte, auf die nur Schreibzugriffe er- laubt sind, als Objekte mit Zugriffsrecht RETURN bezeich- net. RETURN-Parameter und mit RETURN vereinbarte Objekte ha- ben also das Zugriffsrecht RETURN. - 36 - 2.4.1.6 Bedingungen fuer die Parameteruebergabe Damit die Parameteruebergabe legal ist, muessen die nachfol- genden Bedingungen erfuellt sein: A) Der Typ des aktuellen Parameters muss aufwaerts- kompatibel zu dem des formalen Parameters sein. (aktuell <| formal) B) Bei Teilbereichstypen muss der uebergebene Wert im Wertebereich des formalen Parameters liegen. C) Ist das Zugriffsrecht des aktuellen Parameters - CONST, so muss der formale Parameter das Zugriffs- recht CONST haben. - VAR, so darf der formale Parameter jedes Zugriffs- recht haben. - RETURN, so muss der formale Parameter das Zugriffs- recht RETURN haben. - 37 - 2.4.2 Symbol-Parameter Viele Prozeduren verlangen an fixen Stellen in der aktuellen Parameterliste ein bestimmtes Symbol. So verlangt die nach- folgende IF-Prozedur, dass nach der Bedingung das Symbol THEN und nach der Anweisung die Symbole END und IF steht. IF bedingung THEN anweisung END IF Um in der formalen Parameterliste festzulegen, wo solche Symbole fix stehen muessen, gibt es den SYMBOL-Parameter. Die obige IF-Prozedur vereinbart man in MASTER mit: LOCAL CONST PROC: IF (CONST BOOLEAN: bedingung; "THEN"; CONST PROC: anweisung; "END"; "IF") = CASE bedingung OF WHEN {TRUE}: anweisung; WHEN {FALSE}: END CASE; Wie man sieht, besteht ein SYMBOL-Parameter aus einem als Zeichenkette angeschriebenen Symbol. In der obigen Deklara- tion sind also "THEN", "END" und "IF" SYMBOL-Parameter. Aufgerufen wird diese neue Prozedur zum Beispiel mit: IF WERTB) OR (DIRECTION.NUMBER=2) AND (Ab THEN ergebnis:=a; ELSE ergebnis:=b; END IF; END; Die Funktion MAX liefert das Maximum zweier Zahlen. Ein Auf- ruf der Funktion MAX erfolgt zum Beispiel mit MAX (I, J). Funktionen koennen Ergebnisse jeden Typs liefern. Es koennen somit auch Funktionen definiert werden, die zum Beispiel RECORDs, ARRAYs oder TYPEn liefern. 2.5.2 Antifunktionen Eine Funktion erhaelt einen Satz von Parametern, fuehrt einige Berechnungen durch, und liefert ein Ergebnis. Das Ge- genteil einer Funktion ist eine Antifunktion. Eine Antifunk- tion erhaelt einen Wert zugewiesen, fuehrt einige Berechnun- gen durch, und liefert ein Ergebnis ueber Parameter zu- rueck. So soll zum Beispiel die Antifunktion SCREEN alle Zeichenketten, die ihr zugewiesen werden, auf der angegebe- nen Bildschirmposition ausgeben: LOCAL CONST PROC CONST STRING: SCREEN ("["; CONST CARDINAL`RANGE{1..24}: X; ","; CONST CARDINAL`RANGE{1..80}: Y; "]") = BEGIN CONST STRING: ARGUMENT; GOTOXY(X, Y); WRITE(ARGUMENT); END; Die Antifunktion SCREEN ermoeglicht es, dem Bildschirm Zei- chenketten wie einem Array zuzuweisen: SCREEN[10,25]:="THIS TERMINAL WILL EXPLODE IN FIVE SECONDS!"; Antifunktionen haben den Typ PROC CONST eingabetyp. Bei der Definition der Antifunktion SCREEN wurde die vordefinierte BEGIN CONST-Antifunktion verwendet. Die BEGIN CONST-Anti- funktion vereinbart eine Konstante. diese Konstante erhaelt den Wert, den die Antifunktion bei ihrem Aufruf zugewiesen bekommt. - 48 - Es ist jedoch auch moeglich, Antifunktionen ohne Verwendung der BEGIN CONST-Antifunktion zu definieren. zum Beispiel: LOCAL CONST PROC CONST STRING: BILDSCHIRM ('['; CONST CARDINAL`RANGE{1..24}: LINE; ']') = SCREEN [LINE, 1]; Hier wird die Antifunktion BILDSCHIRM durch die Antifunk- tion SCREEN definiert. BILDSCHIRM[1]:="THE BEST WAY TO WRITE CORRECT PROGRAMS"; BILDSCHIRM[2]:="IS TO LEAVE OUT ALL THE BUGS."; Auch die Aenderung von Variablen mittels Antifunktion ist moeglich: LOCAL CONST PROC CONST INTEGER: FI = FILE_INDEX [INDEXNUMBER]; Eine Antifunktion mit einem VAR-Parameter ist zum Beispiel INCR: LOCAL CONST PROC CONST INTEGER: INCR (VAR INTEGER: NUMBER) = BEGIN CONST INTEGER: DIFF; NUMBER:=NUMBER+DIFF; END; Ein Aufruf von INCR erfolgt mit: INCR(ZAHL):=5; 2.5.3 Hybridfunktionen Eine Hybridfunktion ist ein Mittelding zwischen einer Funk- tion und einer Antifunktion. Sie kann sowohl als Funktion als auch als Antifunktion aufgerufen werdne. Hybridfunktio- nen haben den Typ PROC VAR eingabe_und_ergebnistyp. - 49 - Die nachfolgend vereinbarte Hybridfunktion SUBSTR ermoeg- licht einen Zugriff auf Teile von Zeichenketten: LOCAL CONST PROC VAR STRING: SUBSTR ("(", VAR STRING: KETTE; ","; CONST CARDINAL: START; ","; CONST CARDINAL: LEN; ")") = BEGIN VAR STRING: A; KETTE [START .. START+LEN-1] := A; --- A := KETTE [START .. START+LEN-1]; END; Die Hybridfunktion SUBSTR kann zum Beispiel einen Teilstring aus einem bestehenden STRING herauskopieren: ABC := SUBSTR ("THIS SENTENCE HAS FIVE WORDS.", 6, 8); Hier erhaelt ABC den Wert "SENTENCE". Die Hybridfunktion SUBSTR kann aber auch zum Hineinkopieren eines STRINGs in einen anderen STRING verwendet werden: SUBSTR (ABC, 3, 5) := "VEN TIMES A WEEK I READ THIS ARTICL"; ABC hat jetzt den Wert: "SEVEN TIMES A WEEK I READ THIS ARTICLE" Fuer die Definition der Hybridfunktion SUBSTR wurde die vordefinierte BEGIN VAR-Hybridfunktion verwendet. Die BEGIN VAR-Hybridfunktion vereinbart eine locale Variable. Wenn die Hybridfunktion als Antifunktion aufgerufen wird, erhaelt die Variable den Eingangswert und die Anweisungen bis zum --- Symbol werden durchgefuehrt. Wenn die Hybridfunktion als Funktion aufgerufen wird, dann werden die Anweisungen vom --- Symbol bis zum END-Symbol durchgefuehrt und das Ergebnis der Hybridfunktion ist der Wert der lokalen Variablen. Eine weitere Anwendung einer Hybridfunktion ist zum Beispiel das Setzen und Abfragen der Zeit: LOCAL CONST PROC VAR STRING: TIME = BEGIN VAR STRING: A; SETTIME(A); --- RECEIVETIME(A); END; - 50 - 2.5.4 Overloading Unter Overloading versteht man die mehrfache Verwendung des- selben Namens fuer verschiedene Funktionen, Antifunktionen, Hybridfunktionen bzw. Prozeduren. Welches Unterprogramm tatsaechlich aufgerufen wird, wird durch die aktuellen Para- meter entschieden. In MASTER hat jeder Ausdurck genau einen Typ zugeordnet. Jedes Unterprogramm kann somit durch die Ty- pen seiner aktuellen Parameter identifiziert werden. Zwei Unterprogramme ueberladen denselben Namen, wenn sich ihre formalen Parameterlisten nicht ueberdecken. Zwei formale Parameterlisten ueberdecken sich nicht, wenn sich ihre ersten Parameter nicht ueberdecken oder sich ihre zweiten Parameter nicht ueberdecken oder ... ... oder sich ihre n-ten Parameter nicht ueberdecken oder wenn sie eine verschiedene Anzahl von Parametern haben. Zwei formale Parameterlisten ueberdecken sich also, wenn sie die gleiche Anzahl von formalen Parametern haben und der n-te Parameter der ersten Parameterliste den n-ten Parameterliste der zweiten Parameterliste ueberdeckt. Zwei formale Parameter (PAR1 und PAR2) ueberdecken sich - wenn der eine Parameter (PAR1) ein Struktur-Parameter ist, und sich sein erster Parameter (PAR1[1]) mit dem anderen Parameter (PAR") ueberdeckt. - wenn sie Symbolparameter mit identischen Symbolen sind. ( z.B.: "WHEN" und "WHEN" ueberdecken sich, "END" und "ELSE" ueberdecken sich nicht ) - wenn sie normale Parameter oder Initialisierungspara- meter sind und ihre Typen kompatibel ( <|#> oder <#|> oder <||> ) zueinander sind. ( CONST INTEGER: AB; und RETURN CARDINAL: XY ueber- decken sich, CONST BOOLEAN: A; und VAR CHAR: B; ueberdecken sich nicht ) - wenn der eine Parameter ein LOCAL-Parameter ist und der andere Parameter kein Symbol-Parameter ist. In allen anderen Faellen ueberdecken sich zwei formale Para- meter nicht. Fuer die Identifizierung eines Unterprogrammes werden die folgenden Eigenschaften nicht herangezogen: - Das Zugriffsrecht eines normalen Parameters. - Der Name eines formalen Parameters. - Der Ergebnistyp einer Funktion oder Hybridfunktion. - Der Eingabetyp einer Antifunktion oder Hybridfunktion. - Das Zugriffsrecht eines Unterprogrammes. - Die Prioritaet und die Assoziativitaet eines Unterprogrammes (siehe Kapitel 2.5.5). - 51 - 2.5.5 Operatoren Operatoren sind eine Sonderform von Funktionen. Bei Operato- ren spielen Prioritaet und Assoziativitaet eine Rolle. Ope- ratoren mit staerkerer Prioritaet binden ihre Operanden staerker an sich als solche mit schwaecherer Prioritaet. Die Prioritaet ist eine Zahl vom Typ CARDINAL, daher eine natuerliche Zahl (inclusive 0). Die staerkste Prioritaet ist 0, schwaechere Prioritaeten werden durch groessere Zahlen dargestellt. Die Bedeutung des Wortes Binden in diesem Zusammenhang wird durch ein Beispiel erklaert: = A + B = C * D / \ / \ * priority 2 + * + priority 3 / \ / \ = priority 4 A B C D Zuerst erhaelt das *, anschliessend das +, und zuletzt das = seine Operanden. Durch die Prioritaet der Operatoren ist die Bedeutung dieses Ausdrucks eindeutig festgelegt. Nicht fest- gelegt ist jedoch, ob zur Laufzeit des Programmes die + oder die * Operation als erste durchgefuehrt wird. Es gibt in MASTER auch keine Moeglichkeit, dies festzulegen. Die Assoziativitaet legt fest, in welcher Reihenfolge Opera- toren mit der gleichen Prioritaet ihre Operanden an sich binden. Zum Beispiel A - B - C kann auf zwei Arten interpretiert werden (A - B) - C or A - (B - C) Insgesamt gibt es fuer die Assoziativitaet vier Moeglichkeiten: Symbol Binding von links nach rechts (Linksassoziativ). -> Binding von rechts nach links (Rechtsassoziativ). <- Weder der linke noch der rechte Operand dieses Operators duerfen die gleiche Prioritaet wie der Operator haben (Nicht assoziativ). <-> Links dieses Operators wird von links nach rechts gebunden (Links linksassoziativ) und rechts dieses Operators wird von rechts nach links gebunden (Rechts rechtsassoziativ) -><- - 52 - Die letzten zwei Moeglichkeiten scheiden bei dem Subtrak- tionsbeispiel aus, weil sie keine legale Interpretation ergeben. Die dritte Art von Assoziativitaet ( <-> ) hat z.B. der Gleichheitsoperator ( = ) aus PASCAL [Jensen/Wirth 1978], weil ein Ausdruck wie A = B = C dort verboten ist. Die vierte Art von Assoziativitaet ( -><- ) kann wie folgt gedeutet werden: - Wenn ein Praefixoperator die Assoziativitaet -><- hat, dann bindet er seine Operanden von rechts nach links (Rechtsassoziativ). - Wenn ein Postfixoperator die Assoziativitaet -><- hat, dann bindet er seine Operanden von links nach rechts (Linksassoziativ). Fuer die Erklaerung der Assoziativitaet gibt es noch eine zweite Moeglichkeit. Durch die Assoziativitaet wird festge- legt, ob ein Operand eine staerkere Prioritaet (kleinere Prioritaetszahl) als der Operator haben muss, oder ob er auch die Prioritaet des Operators haben darf. Zum Beispiel: - 3 A - B - C / \ / \ / \ <=3 / \ <3 - priority 3 -> / \ / \ - C 3 0 / \ / \ / \ <=3 / \ <3 / \ / \ A B 0 0 Die Ziffern in den Knoten des rechten Baumes geben die Prioritaetszahl des jeweiligen Teilausdruckes (Teilbaumes) an. Durch < und <= wird die jeweils verlangte Bedingung fuer die Prioritaetszahl der Operanden ausgedrueckt. Eine interpretation ist legal, wenn alle diese Bedingungen erfuellt sind. Ergeben sich fuer einen Ausdruck mehrere le- gale Interpretationen oder keine legale Interpretation, so ist der Ausdruck fehlerhaft. In PROLOG [Clocksin/Mellish 1981] wird die Assoziativitaet eines Operators ebenfalls mit Hilfe der Prioritaet des Ope- rators definiert. Einem Operator der Assoziativitaet -> ent- spricht in PROLOG ein mit yfx oder yf spezifizierter Opera- tor. (dabei bedeutet: f ... Operator, x,y ... Operanden, wo- bei x der < -Bedingung und y der <= -Bedingung entspricht) - 53 - Uebersicht ueber alle Moeglichkeiten der Assoziativitaet: +---------------------+---------------------------------+ | Assoziativitaetsart | Die Prioritaetszahl des | +---------------------+----------------+----------------+ | | linken | rechten | | | Operanden muss | Operanden muss | +---------------------+----------------+----------------+ | -> | <= | < | | <- | < | <= | | <-> | < | < | | -><- | <= | <= | +---------------------+----------------+----------------+ | | der des Operators sein. | +---------------------+---------------------------------+ Mit dem linken Operanden eines Operators ist der aktuelle Parameter vor dem Namen des Operators gemeint. Als rechte Operanden eines Operators sind die aktuellen Parameter nach dem letzten Symbol des Operators zu verstehen. Bei den ueb- lichen Operatoren faellt der Name des Operators mit dem letzten Symbol des Operators zusammen. Ist dies nicht der Fall, gibt es noch eine dritte Art von Operanden (Aktuellen Parametern), die wir als mittlere Operanden bezeichnen. Die mittleren Operanden befinden sich also zwischen dem Namen und dem letzten Symbol eines Operators. Mittlere Operanden koennen jede beliebige Prioritaet haben, unabhaengig von der Assoziativitaet des Operators. Es folgt ein praktisches Beispiel: LOCAL CONST PRIORITY 5 -> PROC RETURN BOOLEAN: (CONST BOOLEAN: OPERAND1) XOR (CONST BOOLEAN: OPERAND2) = OPERAND1 <> OPERAND2; Wie man sieht, kann man Prioritaet und Assoziativitaet ein- fach vor dem Symbol PROC hinschreiben. Da Operatoren eigentlich nur mit Prioritaet und Assoziativitaet versehene Funktionen und Funktionen nur ein Sonderfall von Prozeduren sind, kann man auch Prozeduren mit Prioritaet und Assoziativitaet behaften. Dies geschieht auf dieselbe Art und mit derselben Bedeutung wie bei den Opera- toren. Fuer die Anweisungen der ueblichen Programmierspra- chen findet man jedoch mit der Defaultprioritaet und der Defaultassoziativitaet das Auslangen. Umgekehrt kann man schliessen, dass auch Prozeduren und Funktionen, bei denen man keine Prioritaet und Assoziativitaet explizit angibt, eine Prioritaet und eine Assoziativitaet erhalten. Der De- faultwert fuer die Prioritaet ist 0 und derjenige fuer die Assoziativitaet ist -><-. - 54 - Uebrigens haben in MASTER alle Objekte (Variable, Konstante, Typen, Anweisungen, Prozeduren, Funktionen, Operatoren) eine Prioritaet und eine Assoziativitaet. Bei Objekten ohne Para- metern hat die Assoziativitaet keinerlei Bedeutung. Eine Aenderungsmoeglichkeit fuer die Prioritaet und die Assoziativitaet besteht nur fuer Prozeduren und ihre Spezialfaelle (Funktionen, Operatoren). Deshalb haben alle Objekte, bei denen man Prioritaet und Assoziativitaet nicht aendern kann, die Defaultprioritaet und die Default- assoziativitaet. Die vordefinierten Operatoren von MASTER haben die an- schliessend beschriebenen Prioritaeten und Assoziativita- eten: Operator Prioritaet Assoziativitaet [ (Indizieren) 1 -> . (Selektieren) 1 -> ^ (Dereferenzieren) 1 -> ` (Attributzugriff) 1 -> ** ! 2 -> + - (Vorzeichen) 3 <- * / DIV REM MOD 4 -> + - (Infixoperatoren) 5 -> TIMES 6 -> & 7 -> = <> > >= < <= 8 <-> <| |> <# #> 8 <-> <||> <|#> <#|> <##> 8 <-> NOT 9 <- AND AND THEN 10 -> OR OR ELSE 11 -> : (Formatieren) 12 <-> Einige vordefinierte Operatoren sind mit Spezialsymbolen be- zeicnnet. (z.B.: +, -, *, /, **, <=, ...) Spezialsymbole sind Symbole, die mit einem Sonderzeichen (ausgenommen ' und ") beginnen. Wenn ein Operator vereinbart werden soll, der mit einem Spezialsymbol bezeichnet wird, muss man den Opera- tor bei der Vereinbarung als Zeichenkette anschreiben: LOCAL CONST PRIORITY 3 <- PROC RETURN VECTOR: "-" (CONST VECTOR: argument) = BEGIN RETURN VECTOR: ergebnis; ergebnis.x := -argument.x; ergebnis.y := -argument.y; ergebnis.z := -argument.z; END; Auf diese Art ist es ebenfalls moeglich, Prozeduren zu ver- einbaren, die mit Spezialsymbolen bezeichnet werden (z.B.: :=, +:=, *:= oder **ASSERT**) . - 55 - 2.5.6 Klammern Durch runde Klammern wird in den meisten Programmierspra- chen (z.B.: PASCAL [Jensen/Wirth 1978], MODULA [Wirth 1982], ELAN [Hommel/Jaeckel/Jaehnichen/Kleine/Koch/Koster 1979], ADA [ANSI 1983], C [Kernighan/Ritchie 1978]) die durch die Prioritaet der Operatoren vorgegebene Bindungsreihenfolge geaendert. Klammern werden in den ueblichen Programmier- sprachen weder als Funktionen noch als Operatoren angese- hen. Sie fallen voellig aus dem Rahmen und sind wie Anwei- sungen vordefiniert. In MASTER sind fuer die vordefinierten Typen Klammern nach dem folgenden Muster vereinbart: LOCAL CONST PROC RETURN INTEGER: "(" (CONST INTEGER: argument; ")") = argument; 2.5.7 Ausdruecke Ausdruecke sind: - Variable, Konstante und Pseudovariable. - Aufrufe von Funktionen, Antifunktionen und Hybridfunk- tionen. - Aufrufe von Prozeduren. Besteht ein Ausdruck aus einer Variablen, Konstanten oder Pseudovariablen, so hat der Ausdruck den Typ und das Zu- griffsrecht der Variablen, Konstanten bzw. Pseudovariablen. Zum Beispiel: PI CONST REAL NUMBER VAR CARDINAL RESULT RETURN BOOLEAN Wenn ein Ausdruck aus dem Aufruf einer Funktion, Antifunk- tion oder Hybridfunktion besteht, so hat er den Typ und das Zugriffsrecht der Funktion, Antifunktion bzw. Hybridfunk- tion. Zum Beispiel ORD ('1') CONST PROC RETURN CARDINAL F (X, Y) VAR PROC RETURN REAL PRINT CONST PROC CONST STRING Wenn ein Ausdruck aus dem Aufruf einer Prozedur besteht, so hat er den Typ und das Zugriffsrecht der Prozedur. Zum Bei- spiel: WRITE("TIMBER!"); CONST PROC STATEMENT VAR PROC Es kann somit jedem Ausdruck genau ein Typ und genau ein Zu- griffsrecht zugeordnet werden. - 56 - Aufrufe von Funktionen, Antifunktionen und Hybridfunktionen sind auswertbare Ausdruecke. Bei der Auswertung eines Aus- drucks aendert sich das Zugriffsrecht und der Typ des Aus- drucks. Auswerten von konstanten Funktionen, Hybridfunktionen und Antifunktionen: CONST PROC RETURN INTEGER ---> CONST INTEGER CONST PROC VAR INTEGER ---> VAR INTEGER CONST PROC CONST INTEGER ---> RETURN INTEGER Auswerten von variablen Funktionen, Hybridfunktionen und Antifunktionen: VAR PROC RETURN INTEGER ---> CONST INTEGER VAR PROC VAR INTEGER ---> VAR INTEGER VAR PROC CONST INTEGER ---> RETURN INTEGER Konstante, Variable und Pseudovariable sind algorithmisier- bare Ausdruecke. Das bedeutet, man kann solche Ausdruecke auch als Funktionen, Hybridfunktionen und Antifunktionen auffassen. Beim Algorithmisieren von Ausdruecken aendert sich der Typ und das Zugriffsrecht des Ausdruckes. Algorithmisieren von Konstanten, Variablen und Pseudovaria- blen: CONST INTEGER ---> CONST PROC RETURN INTEGER VAR INTEGER ---> CONST PROC VAR INTEGER RETURN INTEGER ---> CONST PROC CONST INTEGER Fuer die Parameteruebergabe muss der Typ des aktuellen Para- meters aufwaertskompatibel zum Typ des formalen Parameters sein. Wenn ein Ausdruck nicht kompatibel zu einem formalen Parameter ist, so wird der Ausdruck A) wenn er eine Konstante, Variable oder Pseudovariable ist, algorithmisiert. B) wenn er eine Funktion, Hybridfunktion oder Antifunk- tion ist, so lange ausgewertet bis Kompatibilitaet herrscht oder ein nicht auswertbarer Ausdruck vor- liegt. Syntax: expressiion ::= [ expression ] identifier { argument } . argument ::= expression | symbol | identifier_declaration . Wenn auswertbare Ausdruecke als Parameter uebergeben werden, muessen zusaetzlich zu den normalen Bedingungen fuer die Parameteruebergabe noch einige Bedingungen gelten. - 57 - Uebersichtstabelle fuer die Parameteruebergabe bei normalen Parametern: +--------++--------------------------------------------------+ | \ || a k t u e l l e r P a r a m e t e r | |for-\ ++--------------------------------------------------+ |maler\ || ||CONST PROC || VAR PROC ||RETURN PROC| |Para- \ ++---+---+---++---+---+---++---+---+---++---+---+---+ |meter \||C I|V I|R I||C I|V I|R I||C I|V I|R I||C I|V I|R I| +========++===+===+===++===+===+===++===+===+===++===+===+===+ |C I ||>I |>I | || |>I |>I || |>I |>I || | | | +--------++---+---+---++---+---+---++---+---+---++---+---+---+ |V I || |>I>| || |>I>| || |>I>| || | | | +--------++---+---+---++---+---+---++---+---+---++---+---+---+ |R I || | I>| I>|| I>| I>| || I>| I>| || | | | +========++===+===+===++===+===+===++===+===+===++===+===+===+ |C P C I || |>P |>P ||>P |>P | ||>P |>P | || | | | +--------++---+---+---++---+---+---++---+---+---++---+---+---+ |C P V I || |>P | || |>P | || |>P | || | | | +--------++---+---+---++---+---+---++---+---+---++---+---+---+ |C P R I ||>P |>P | || |>P |>P || |>P |>P || | | | +========++===+===+===++===+===+===++===+===+===++===+===+===+ |V P C I || | | || | | ||>P>| | || | | | +--------++---+---+---++---+---+---++---+---+---++---+---+---+ |V P V I || | | || | | || |>P>| || | | | +--------++---+---+---++---+---+---++---+---+---++---+---+---+ |V P R I || | | || | | || | |>P>|| | | | +========++===+===+===++===+===+===++===+===+===++===+===+===+ |R P C I || | | || | | || P>| | || P>| | | +--------++---+---+---++---+---+---++---+---+---++---+---+---+ |R P V I || | | || | | || P>| P>| P>|| P>| P>| P>| +--------++---+---+---++---+---+---++---+---+---++---+---+---+ |R P R I || | | || | | || | | P>|| | | P>| +========++===+===+===++===+===+===++===+===+===++===+===+===+ Legende: C ... CONST I ... INTEGER V ... VAR P ... PROC R ... RETURN C P R I bedeutet: CONST PROC RETURN INTEGER CONST PROC V I bedeutet: CONST PROC VAR INTEGER >I Ein INTEGER-Wert wird an das Unterprogramm uebergeben. >I> Ein INTEGER-Wert wird an das Unterprogramm uebergeben. Am Ende des Unerprogrammes wird ein INTEGER-Wert vom Unterprogramm zurueckgegeben. I> Am Ende des Unerprogrammes wird ein INTEGER-Wert vom Unterprogramm zurueckgegeben. >P Ein auswertbarer Ausdruck wird an das Unterprogramm uebergeben. - 58 - >P> Ein auswertbarer Ausdruck wird an das Unterprogramm uebergeben. Am Ende des Unerprogrammes wird ein auswertbarer a Ausdruck vom Unterprogramm zurueckgegeben. P> Am Ende des Unerprogrammes wird ein auswertbarer Ausdruck vom Unterprogramm zurueckgegeben. leeres Feld Diese Parameteruebergabe ist verboten. - 59 - 2.6 MODULE ========== Module ermoeglichen es, eine Gruppe von Deklarationen zusammen- zufassen und vom Rest des Programmes abzugrenzen. Gleichzeitig mit dieser Abgrenzung kann festgelegt werden, dass von ausser- halb des Moduls nur auf bestimmte Objekte des Moduls zugegrif- fen werden darf, waehrend andere Objekte verborgen bleiben. Ein Modul ermoeglicht es also, Datenstrukturen und Zugriffsprozedu- ren zur Verfuegung zu stellen und gleichzeitig die interne Struktur der Daten und die benoetigten Hilfsprozeduren zu verstecken. 2.6.1 Einfache Module Im nachfolgenden Beispiel wird ein Modul fuer einen Stapel von Zeichen vereinbart. Die Variable ELEMENT enthaelt jenes Element des Stapels, auf das gerade zugegriffen werden darf. Mit den Prozeduren PUSH und POP kann das ELEMENT auf den Stapel gelegt beziehungswiese vom Stapel genommen wer- den. Mit den Funktionen FULL und EMPTY kann abgefragt wer- den, ob der Stapel voll beziehungsweise leer ist. LOCAL CONST MODULE: STACK EXPORT VAR CHAR: ELEMENT; CONST PROC: PUSH, POP; CONST PROC RETURN BOOLEAN: FULL, EMPTY; END MODULE = DECLARE CONST CARDINAL: MAX = 100; VAR CHAR: ELEMENT := ' '; CARDINAL`RANGE{0..MAX}: INDEX := 0; ARRAY CARDINAL`RANGE{1..MAX} OF CHAR: FELD; CONST PROC: PUSH = IF INDEX0 THEN ELEMENT:=FELD[INDEX]; INDEX:=INDEX-1; ELSE HALT; END IF; CONST PROC RETURN BOOLEAN: FULL = INDEX = MAX; CONST PROC RETURN BOOLEAN: EMPTY = INDEX = 0; END DECLARE; - 60 - Die Vereinbarung des Moduls STACK besteht aus zwei Teilen, dem Modulkopf (zwischen STACK und END MODULE) und dem Modul- rumpf (zwischen DECLARE und END DECLARE). Im Modulkopf stehen die Koepfe jener Objekte, auf die von ausserhalb des Moduls zugegriffen werden kann. Von aussen kann man mit ELEMENT auf das Element zugreifen, mit den Prozeduren PUSH und POP koennen die Stapeloperationen durchgefuehrt werden und mit FULL und und EMPTY kann abge- fragt werden, ob der STACK voll beziehungsweise leer ist. Der Modulrumpf des Moduls STACK besteht aus dem vordefinier- ten DECLARE-Modul. Das DECLARE-Modul enthaelt die voll- staendigen Deklarationen der Objekte, die STACK exportiert, und die Deklarationen von Hilfsobjekten, die ausserhalb von STACK nicht zugaenglich sind. Solche Hilfsobjekte sind MAX, INDEX und FELD. Wie gewuenscht kann auf die Objekte MAX, INDEX und FELD von aussen nicht zugegriffen werden. Ein Anwendungsbeispiel fuer den STACK ist: WHILE NOT (EOLN OR FULL) DO READ(ELEMENT); PUSH; END WHILE; READLN; WHILE NOT EMPTY DO POP; WRITE(ELEMENT); END WHILE; WRITELN; Das obige Programmstueck liest eine Zeile ein und schreibt sie verkehrt aus. Wenn die Zeile nicht im STACK unterge- bracht werden kann (FULL), dann werden nur die ersten MAX Zeichen der Zeile verkehrt ausgegeben. 2.6.2 Qualifiziert exportierende Module Die durch das Modul STACK des letzten Kapitels definierten Objekte koennen mit eventuell schon vorhandenen Objekten gleichen Namens verwechselt werden. Um zu vermeiden, dass eine Verwechslung stattfindet, kann man die Objekte des Mo- duls QUALIFIED exportieren. Das bedeutet, dass die durch das Modul exportierten Objekte ausserhalb des Moduls durch die Angabe des Modulnamens gekennzeichnet werden muessen. Zum Beispiel: STACK.EMPTY oder STACK.PUSH. - 61 - Um Objekte QUALIFIED zu exportieren, muss im Modulkopf nach dem Symbol EXPORT das Symbol QUALIFIED stahen. LOCAL CONST MODULE: STACK EXPORT QUALIFIED VAR CHAR: ELEMENT; CONST PROC: PUSH, POP; CONST PROC RETURN BOOLEAN: FULL, EMPTY; END MODULE = DECLARE (* Als Modulrumpf kann der aus dem *) (* Kapitel 2.6.2 verwendet werden. *) END DECLARE; Bei diesem STACK muessen das Element und die Stapeloperatio- nen mit STACK.ELEMENT, STACK.PUSH, STACK.POP, STACK.FULL und STACK.EMPTY angesprochen werden. Um ELEMENT, PUSH, POP und EMPTY direkt anzuschreiben, kann man die WITH-Anweisung benutzen: WITH STACK DO POP; character:=ELEMENT; PUSH; END WITH; Semantik: Die zwischen DO und END stehende Folge von Anweisungen wird durchgefuehrt. Innerhalb der WITH-Anweisung koennen die mit OUALIFIED exportierten Objekte der zwischen WITH und DO angegebenen MODULE direkt (ohne . ) verwendet werden. Syntax: with_statement ::= 'WITH' identifier { ',' identifier } 'DO' statement_sequence 'END' 'WITH' . - 62 - 2.6.3 Gemischt exportierende Module Es gibt fuer ein Modul auch die Moeglichkeit, einige Objekte mit ind einige Objekte ohne QUALIFIED, zu exportieren. Zum Beispiel: LOCAL CONST MODULE: STACK EXPORT CONST PROC: PUSH ("("; CONST CHAR: ELEM; ")"), POP ("("; RETURN CHAR: ELEM; ")"); EXPORT QUALIFIED CONST PROC RETURN BOOLEAN: FULL, EMPTY; CONST PROC RETURN CHAR: ELEMENT; END MODULE = DECLARE CONST CARDINAL: MAX = 100; VAR CARDINAL`RANGE{0..MAX}: INDEX := 0; ARRAY CARDINAL`RANGE{1..MAX} OF CHAR: FELD; CONST PROC: PUSH ("("; CONST CHAR: ELEM; ")") = IF INDEX0 THEN ELEM:=FELD[INDEX]; INDEX:=INDEX-1; ELSE HALT; END IF; CONST PROC RETURN BOOLEAN: FULL = INDEX = MAX; CONST PROC RETURN BOOLEAN: EMPTY = INDEX = 0;