Home Up Intro Contents Chapter 1 2 3 4 5 6 7 8 9 10 Design Assert Timing EBNF Report Pas Last Changed: Nov. 19, 1997
This is a conversion from Oberon text to HTML. The converter software is still under development, and some features or information may be missing in this converted version. HTML hypertext facilities are not yet active in this document. To exploit the interactive facilities, use Oberon System 3 and the source of this text, available as ASCII-coded Oberon System 3 documents. A previous version is also available for Oberon V4. To access this and other additional material use ftp.
For the convenience of our students, most of this information and the related material is in German. Sorry if this is not one of your languages.

Einführung in die Programmiersprache Oberon.

G. Sawitzki <gs@statlab.uni-heidelberg.de>



&P: Oberon für Pascal-Programmierer

Dieses Kapitel soll Pascal- oder Modula-Programmierern einen schnellen Einstieg in die Programmiersprache Oberon ermöglichen. Nur einige knappe Erklärungen werden hier gegeben. Pascal- oder Modula-Programmierer werden sicherlich ihren eigenen Weg durch den Oberon-Kurs
  <"http://statlab.uni-heidelberg.de/projects/oberon/kurs/">
finden. Dieses Kapitel soll auf die besonderen Aspekte hinweisen, die sonst erst gesucht werden müssen.

Oberon gehört zu einer Familie von Programmiersprachen, deren ältere Mitglieder Algol, Pascal und Modula sind. Wir konzentrieren uns hier auf die aktuelle Variante Oberon-2. Die wichtigsten Eigenschaften von Oberon-2 sind Block-Struktur, Modularität, getrennte Compilierbarkeit, statische Datentypen mit starker Typen-Überprüfung (auch über Modul-Grenzen hinweg, und erweiterbare Datentypen mit typen-gebundenen Prozeduren. Typen-gebundene Prozeduren sind eine Besonderheit bei Oberon-2; die anderen Merkmale gibt es auch in der ursprünglichen Oberon-Variante.

Oberon ist auch der Name eines Betriebssystems (geschrieben in der Sprache Oberon). In diesem Kapitel konzentrieren wir uns jedoch auf die Sprache Oberon.


Ein Artikel von N. Wirth, der die Unterschiede zwischen Modula-2 und Oberon beschreibt, ist in
<ftp://statlab.uni-heidelberg.de/pub/mirrors/inf.ethz.ch/oberon/docu/>
zu finden. Ein Modula->Oberon "Übersetzer" von N. Wirth ist ebenfalls verfügbar. Pascal-Programme können grossenteils automatisch mit dem Kommando
  Mess.Style * \i=P \o=O ~
konvertiert werden, wenn Mess und MessP unter dem Oberon-System installiert sind. Mess ist in
  <http://statlab.uni-heidelberg.de/projects/oberon/util/>
zu finden.



Wir nehmen einen eher informellen Standpunkt im Hinblick auf Pascal ein. Pascal ist formell im Pascal-Report definiert, ergänzt durch den Object Pascal Report, der die Sprache um Konstrukte für objekt-orientierte Programmierung erweitert. In der Praxis gibt es jedoch eine Reihe von abweichenden Implementierungen, bis hin zu Eigentümlichkeiten einzelner Compiler wie Turbo Pascal, so dass ein eher pragmatisches Herangehen geraten scheint.

Kleine Unterschiede
Im Vergleich zu Modula und Pascal ist Oberon aufgeräumt. Selten gebrauchte oder schwierig zu implementierende Sprachelemente sind entfernt. Sprachelemente, die allgemein eine häufige Fehlerquelle sind, wurden reduziert. Sprachelemente, die den Compilierungsprozess langwierig machen, ohne einen erkennbaren Vorteil zu bringen, sind gestrichen.

Zu den Bereinigungen gehört:

- Namen sind gross/klein-sensitiv. Alle reservierten Namen werden in Gross-Schreibung benutzt.
  Beispiel:
    BEGIN    reserviert
    begin    #BEGIN. Frei-definierter Name

- Zusammengesetzte Anweisungen benutzen immer eine Anweisungsfolge. Die Alternative "Anweisung/Anweisungsfolge" entfällt. Ebenso entfällt ein einleitendes "BEGIN": der Beginn ist bereits durch die zusammengesetzte Anweisung markiert.
  Beispiel:
    IF i>0 THEN
      dothis;
      dothat
    END;
  bzw.
    IF i>0 THEN
      dothis
    END;
Für IF-Kaskaden gibt es ELSIF
  Beispiel:
    IF i>0 THEN
      dothis
    ELSIF i=0 THEN
      dothat
    ELSE
      dosomethingcompletelydifferent
    END;

- Alle Arrays sind 0-basiert. Bei der Deklaration wird die Länge angegeben (nicht das höchste Element).
  Beispiel:
    VAR a: ARRAY 10 OF INTEGER;
  enthält a[0]...a[9].

- Logische Operationen werden von links nach rechts abgearbeitet und abgebrochen, sobald das Resultat bestimmt ist.
  Beispiel:
    IF NoDiskError & EraseOk THEN ... END;
  ruft EraseOk nur auf, wenn zuvor NoDiskError das Resultat
  TRUE ergeben hat.

- Prozedur-Blöcke sind von zusammengesetzten Anweisungen unterschieden. Prozedur-Blöcke enden immer mit einer Identifikation, die eine Synchronisation mit dem Kopf erlaubt.
  Beispiel:
    PROCEDURE xyz;
    BEGIN
      ...
    END xyz;

- GOTO gibt es nicht mehr. Um aus einer Schleife auszubrechen gibt es EXIT; um aus einem Programm auszubrechen gibt es HALT.
Typen sind erweiterbar. Das Typen-System ist so definiert, dass seine Konsistenz einfach zu überprüfen ist und eine effektive Implementierung möglich ist.
  - Aufzählungstypen gibt es nicht mehr.
  - Teilmengen-Typen für ganze Zahlen gibt es nicht mehr.
  - PACKED gibt es nicht mehr. Anstelle dessen gibt es
    Standardtypen CHAR oder SHORTINT zur
    ökonomischen Speicherung.
  - SET ist immer eine Teilmenge der natürlichen Zahlen;
    der maximale Umfang ist implementierungsspezifisch.
    Typisch sind Bereiche wie 0..31 oder 0..63.
  - die Regeln für Typenverträglichkeit sind klarer gefasst.
  - RECORD-Varianten gibt es nicht mehr. Anstelle dessen sind
    alle RECORD-Typen erweiterbar.

Die Syntax von Prozeduren ist vereinheitlicht. Es gibt keine Sonderfälle für Ein-/Ausgabe auf Sprachebene. Ein-/Ausgabe-Prozeduren sind Dienste, die von "Bibliotheksprozeduren" erbracht werden. Dadurch sind jedoch Ein-/Ausgabe-Prozeduren implementationsabhängig und nicht einheitlich.

Grosse Unterschiede

Separate Compilierbarkeit ist eine herausragende Besonderheit von Oberon. Die Programm-Organisation ist grundsätzlich verschieden von Pascal. In Pascal hat das "Hauptprogramm" bereits weitgehend seine Bedeutung verloren und ist oft zu einem Rumpf reduziert. Aufgeräumte Pascal-Programme haben häufig die Struktur
  PROGRAM xxx;
    ...
    VAR abc: xxxType;
    PROCEDURE Init;
    BEGIN
      ...
    END;

    PROCEDURE DoTheJob;
    BEGIN
      ...
    END;

    PROCEDURE Exit;
    BEGIN
      ...
    END;
  ...
  BEGIN
    Init;
    DoTheJob;
    Exit
  END.

In Oberon ist diese Struktur reduziert zu
  MODULE xxx;
    ...
    VAR abc*: xxxType;
    PROCEDURE Init;
    BEGIN
      ...
    END Init;

    PROCEDURE DoTheJob*;
    BEGIN
      ...
    END DoTheJob;

    PROCEDURE Exit*;
    BEGIN
      ...
    END Exit;
  ...
  BEGIN
    Init;
  END xxx.

Oberon-Programme können genau so aufgebaut werden wie Pascal-Programme. Die separate Compilierung gibt zusätzliche Möglichkeiten. Diese werden in Beziehung zum Oberon-System deutlich. In herkömmlichen Systemen sind Programme flüchtig, das heisst nach Ende des Programmes ist alle Programminformation verloren, sofern sie nicht ausgedruckt, gespeichert oder sonst wie gesichert ist. In Oberon tritt an die Stelle eines Programmes ein Modul. Die Information eines Moduls bleibt erhalten, bis das Modul explizit entladen wird.
Als Folge können in Oberon mehrere Module gleichzeitig präsent sein. Module können auf die vorhandene Information wechselseitig zugreifen. Um Information aus verschiedenen Modulen zu unterscheiden, ist das Namenssystem erweitert. Oberon-Namen können einfache Bezeichner sein (wie in Pascal), oder qualifizierte Namen. Qualifizierte Namen haben die Form
  <Modul-Name>.<Variablen-Name>.

Die Information unterliegt einer Zugriffskontrolle auf Modul-Ebene. Eine Markierung * hinter dem Namen in einer Deklaration markiert, dass die Information öffentlich ist. Eine Markierung - markiert, dass sie öffentlich lesbar, aber nicht veränderbar ist.

Die Variable abc zum Beispiel kann im Modul xxx als abc angesprochen werden wie in Pascal. In Oberon kann die Variable abc jedoch auch von jedem anderen gleichzeitig aktiven Modul angesprochen werden, wenn sie exportiert ist. Dazu wird der qualifizierte Name benutzt, das heisst andere Module benutzen abc in der qualifizierten Form als xxx.abc.
  abc:=kiki    innerhalb von xxx
  xxx.abc:=kiki    ausserhalb von xxx
Für die Programmierung hat dies zwei Konsequenzen:
-  es ist nicht mehr notwendig, Daten zwischenzuspeichern, um sie zwischen verschiedenen Teilprogrammen auszutauschen.
-   "Programme" können ohne zusätzlichen Aufwand in Komponenten aufgeteilt werden, die sich auf Teilfunktionen beschränken.

Neben diesem fundamentalen Unterschied, dem Abschied vom globalen Programm zugunsten von Modul-Komponenten gibt es zwei weitere Veränderungen, die Oberon zu einer objekt-orientierten Programmiersprache machen: RECORDs sind erweiterbar und Prozeduren sind vollwertige Mitglieder der Typenfamilie.

RECORDs sind erweiterbar: RECORD-Datentypen bilden eine Erblichkeits-Hierarchie. Abgeleitete Datentypen erben alle Felder ihrer Ahnen, und können zusätzliche Datenfelder hinzufügen. Deklarationen für abgeleitete Datentypen haben die Form
  NewRecord=RECORD (oldrecord)
    NewField: NewFieldTyp
    ...
  END;

Prozedurtypen sind gleichwertig mit anderen Typen. Insbesondere können Prozeduren nicht nur als Parameter übergeben werden, sondern auch in Variablen gespeichert werden. Damit ist es auch möglich, Prozeduren als Methoden mit den zugehörigen Daten in einem RECORD-Typ zu verkapseln, das heisst objekt-orientierte Programmierung wird unterstützt.
Beispiel:
  Handler*=PROCEDURE(obj: Object; VAR M: ObjMsg);
  ObjMsg*=RECORD
      ...
    END;
  Object*=POINTER TO ObjDesc;
  ObjDesc*=RECORD
    ...
    handle*: Handler
    ...
  END;

Müll

Kaum jemand räumt gerne hinter sich auf. Bei der Programmierung ist das Aufräumen, das heisst das Freigeben nicht mehr benutzter Variablen und anderer Resourcen, besonders leidlich. In klassischen Umgebungen ist es üblich, mit diesem Problem lässig umzugehen: man verlässt sich darauf, dass das Betriebssystem nach Programmende aufräumt. Oder man verlässt sich darauf, dass genug Kernspeicher zur Verfügung steht, um das Programm noch laufen zu lassen...
Auf Prozedur-Ebene sorgt die Block-Struktur von Oberon, wie bei allen Sprachen der Oberon-Familie dafür, dass der Speicherplatz richtig verwaltet wird. Wenn Speicherplatz unter Programm-Kontrolle explizit angefordert wird, zum Beispiel mit NEW, kann jedoch ein Verweis auf den Speicherplatz als Parameter, Variablenwert oder anderes weitergegeben werden, ohne dass das Betriebssystem Kontrolle darüber hat.
Der übliche Weg war es, die Speicherverwaltung dem Benutzer zu überlassen. Der Benutzer allokiert Speicherplatz mit Prozeduren wie NEW, und gibt ihn wieder frei mit Prozeduren wie DISPOSE. Dies führt bereits bei klassischen Programmen zu Problemen. Der Benutzer kann Speicherplatz nutzen, ohne ihn angefordert zu haben. Diese Situation ist in der Regel erkennbar, so dass sie den Compilerbauern überlassen werden kann. Das andere Problem ist, dass Speicherplatz freigegeben wird, obwohl er später noch benutzt wird, oder Speicherplatz wird nicht freigegeben, obwohl er nie mehr benutzt wird. Das erste Problem führt in der Regel zu der als "dangling pointers" bekannten Situation, das zweite Problem zu "memory leakage".
In Oberon wird dieses Problem dadurch vermieden, dass "garbage collection" grundsätzlich dem System überlassen wird. Es gibt eine "NEW"-Prozedur wie in Pascal, aber kein "DISPOSE". Das System ist dafür verantwortlich, Speicherplatz frei zu geben, wenn das System beweisen kann, dass der Speicherplatz nicht mehr erreichbar ist.
Der Programmierer kann dem System die Aufgabe leichter machen, indem dynamische Variable auf NIL gesetzt werden, sobald sie nicht mehr benötigt werden.
In einigen Implementierungen gibt es eine Schwachstelle: Oberon erlaubt es, Module explizit freizugeben. Jedes Modul kann aber Prozeduren enthalten, auf die eine Referenz in Variablen gespeichert werden können. Während direkte Prozedur-Referenzen von praktisch allen Implementierungen korrekt behandelt werden, gibt es oft Schwierigkeiten, wenn Prozedur-Referenzen in Variablen gespeichert sind. Oberon sieht vor, Prozeduren, die über Variablen übergeben werden, mit einem * nach dem reservierten Wort PROCEDURE zu kennzeichnen. Von dieser Möglichkeit sollte Gebrauch gemacht werden, um diese Fehlerquelle zu vermeiden.

Vordefinierte Prozeduren

Eine kleine Gruppe von Prozeduren spielt eine Sonderrolle. Diese Prozeduren sind im Oberon-Report definiert (Sektion 10.3) und in jedem Oberon-System definiert. Zum Teil sind die Typen- Verträglichkeitsbedingungen für diese Prozeduren etwas weiter gefasst. Im Vergleich zu PASCAL gibt es einige Ersetzungen:
- PRED und SUCC werden durch die allgemeineren Prozeduren
DEC und INC ersetzt.
- ROUND gibt es nicht mehr. Als allgemeine Konversion ist nur ENTIER verfügbar.


Name    Function
ABS(x)  absolute value
ASH(x, n)  arithmetic shift (x * 2n)
CAP(x)  x is letter: corresponding capital letter
CHR(x)  character with ordinal number x
ENTIER(x)  largest integer not greater than x
LEN(v, n)  length of v in dimension n (first dimension = 0)
LEN(v)  equivalent to LEN(v, 0)
LONG(x)  identity, or extension
MAX(T)  maximum value of type T
MIN(T)  minimum value of type T
ODD(x)  x MOD 2 = 1
ORD(x)  ordinal number of x
SHORT(x)  identity, or restriction
SIZE(T)  number of bytes required by T

Proper procedures

Name  Function
ASSERT(x)  terminate program execution if not x
ASSERT(x, n) terminate program execution if not x
COPY(x, v)  v := x
DEC(v)  v := v - 1
DEC(v, n)  v := v - n
EXCL(v, x)  v := v - {x}
HALT(n)  terminate program execution
INC(v)  v := v + 1
INC(v, n)  v := v + n
INCL(v, x)  v := v + {x}
NEW(v)  allocate v ^
NEW(v, x0, ..., allocate v ^ with lengths x0.. xn

Oberon-Systeme und Bibliotheken

In Oberon gibt es keinen prinzipiellen Unterschied zwischen System, Bibliotheken und Benutzer-Programmen. Jedes dieser Elemente ist über Module realisiert, und jedes Modul kann auf Dienste anderer Module zugreifen. Der Zugriff ist auf Modul kontrolliert. Jedes Modul spezifiziert, welche Information exportiert wird. Nur Information (Konstanten, Typen-Definitionen, Variablen, Prozeduren) auf der obersten Ebene können exportiert werden. Die Exportbedingungen werden durch eine Markierung nach dem Namen in der Deklaration gekennzeichnet. Mit "*" markierte Namen werden allgemein exportiert; mit "-" markierte Namen werden nur zum Lesen freigegeben.

Ein importierendes Modul führt alle Module, von denen importiert wird, in einer Importliste auf. Eine Import-Anweisung hat die Form
  IMPORT <name>[ , <name>] ;
Dabei können "alias"-Namen vegeben werden. Dann hat <name> die Form
  <alias-name>:=<orig. name>

Die verfügbaren Module sind implementationsspezifisch. Die wichtigsten Implementierungen sind (Stand: 15.10.97)
  Oberon V4  die aktuelle Implementierung des
    ursprünglichen   Oberon-Systems.
    Praktisch auf allen Architekturen verfügbar.
  Oberon System 3  eine frühere Variante von Oberon.
    Ein grosser Teil der Oberon-Entwicklung
    konzentriert sich auf System 3, so dass
    hier zum Teil die wichtigsten
    Entwicklungen   zu finden sind.
    Vefügbar auf MS-DOS, LINUX,
    Windows, Mac.
    Punktuelle Differenzen zu Oberon V4,
    zahlreiche Erweiterungen.
  Oberon/F  ein Oberon-basiertes "programming
    framework" mit zahlreichen Erweiterungen.
    Nur auf Windows und Mac.
    Praktisch nicht verträglich mit Oberon V4
    oder System 3.
  Jede Implementierung stellt ihre Bibliotheken bereit, die leider selbst in der Basis nicht überein stimmen. Deshalb ist der Anwender hier jeweils auf die spezielle Implementierung angewiesen.

Für die Ein- und Ausgabe gibt es einfache Modelle, die im Buch von Reiser&Wirth vorgestellt werden. Praktisch jede Implementierung bietet Zugang zu diesen Modellen. Sie können benutzt werden, wenn im benutzenden Moduln die folgenden Module als importiert aufgeführt werden:
  In    Eingabe
  Out    Ausgabe
  XYplane  graphische Ausgabe.
Abhängig von der Implementierung muss vor der ersten Benutzung eine Initialisierung erfolgen mit
  In.Open  (resp. Out.Open, XYPlot.Open).
Die üblichen Dienste sind:

DEFINITION In;
  VAR
    Done*: BOOLEAN;

  PROCEDURE Char*(VAR ch: CHAR);
  PROCEDURE Int*(VAR i: INTEGER);
  PROCEDURE LongInt*(VAR i: LONGINT);
  PROCEDURE LongReal*(VAR y: LONGREAL);
  PROCEDURE Name*(VAR name: ARRAY OF CHAR);
  PROCEDURE Open*;
  PROCEDURE Real*(VAR x: REAL);
  PROCEDURE String*(VAR str: ARRAY OF CHAR)
END In.

DEFINITION Out;

  PROCEDURE Char*(ch: CHAR);
  PROCEDURE Int*(i, n: LONGINT);
  PROCEDURE Ln*;
  PROCEDURE LongReal*(x: LONGREAL; n: INTEGER);
  PROCEDURE Open*;
  PROCEDURE Real*(x: REAL; n: INTEGER);
  PROCEDURE String*(str: ARRAY OF CHAR);

END Out.

DEFINITION XYplane;

  IMPORT
    Objects, Files;

  CONST
    draw*=1;
    erase*=0;

  VAR
    H*: INTEGER;
    W*: INTEGER;
    X*: INTEGER;
    Y*: INTEGER;

  PROCEDURE Clear*;
  PROCEDURE Dot*(x, y, mode: INTEGER);
  PROCEDURE IsDot*(x, y: INTEGER): BOOLEAN;
  PROCEDURE Key*(): CHAR;
  PROCEDURE Open*;
  PROCEDURE XYhandle*(F: Objects.Object; VAR M: Objects.ObjMsg);

END XYplane.

Der fortgeschrittene Programmierer wird diese Module vermeiden. Es sind Übungsbeispiele für die Ausbildung, die durch flexiblere und effizientere Modelle ersetzt werden. Oberon V4 und S3 stellen im Modul Texts und Display (bzw. Display3) Basiselement für Text- und Grafik-Ein/Ausgabe bereit.

Details

Wir geben hier eine kommentierte formale Definition in der OberonBackus-Naur-Form. Das unkommentierte Original ist im Anhang des Oberon-Reports zu finden.

Die elementaren Elemente sind durch folgende Definitionen bestimmt:

ident = letter {letter | digit}.
number   = integer | real.
integer   = digit {digit} | digit {hexDigit} "H".
real   = digit {digit} "." {digit} [ScaleFactor].
ScaleFactor   = ("E" | "D") ["+" | "-"] digit {digit}.

hexDigit   = digit | "A" | "B" | "C" | "D" | "E" | "F".
digit   = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9".
Die Zahlentypen sind
  SHORTINT INTEGER LONGINT REAL LONGREAL.
1.4E3 bezeichnet eine REAL-Konstante, 1.4D3 eine LONGREAL-Konstante. Die Umwandlung von einem niedrigeren in einen höheren Datentypen geschieht automatisch. Von einem höheren (längeren) Datentyp in einen Niedrigeren kann die Umwandlung mit SHORT erreicht werden. Um von REAL zu einem Integer-Typen überzugehen, ist ENTIER als explizite Umwandlung notwendig.
Warnung: Die höheren Datentypen umfassen jeweils den Wertebereich der niedrigeren. Es ist aber nicht garantiert, dass die Genauigkeit die des niedrigeren Datentyps umfasst. Insbesondere ist im allgemeinen die Genauigkeit bei REAL-Zahlen niedriger als bei LONGINT.

Hexadezimalzahlen können mit der H-Notation angegeben werden, zum Beispiel 0FFH.

characterConst = digit {hexDigit} "X" | '"' character '"'..

string = ' " ' {char} ' " ' | " ' " {char} " ' ".
Character-Konstanten können hexadezimal (zum Beispiel 0X, 0DX) oder als Zeichen (zum Beispiel "A") angegeben werden. String-Konstanten können damit " oder ' enthalten, aber nicht beides.

String-Variable werden konventionell als ARRAY OF CHAR mit abschliessendem 0X gespeichert.

Kommentare werden durch (* ... *) abgegrenzt und können geschachtelt sein.

Module  =   MODULE ident ";" [ImportList] DeclSeq [BEGIN StatementSeq] END ident ".".
Das MODUL tritt an die Stelle des Programms. Module sind resident und können Information wechselseitig zugänglich machen.

ImportList   =   IMPORT [ident ":="] ident {"," [ident ":="] ident} ";".
Module können sich wechselseitig importieren, sofern dadurch keine "Import-Zyklen" entstehen. Importierte Module müssen deklariert werden. Dies geschieht in der Importlist. Dabei können Moduln interne Alias-Namen zugeordnet werden, z. B. Tx:=Text;
DeclSeq   =   { CONST {ConstDecl ";" } | TYPE {TypeDecl ";"} | VAR {VarDecl ";"}} {ProcDecl ";" | ForwardDecl ";"}.
ConstDecl  =   IdentDef "=" ConstExpr.
TypeDecl  =   IdentDef "=" Type.
VarDecl  =   IdentList ":" Type.
ProcDecl   =   PROCEDURE [Receiver] IdentDef
  [FormalPars] ";" DeclSeq
  [BEGIN StatementSeq] END ident.
Prozedur-Deklarationen enden mit einem END, dass mit dem Prozedurname markiert ist. Es gibt keinen getrennten Typ für Funktionen: Funktionen sind Spezialfälle von Prozeduren, die eine Resultat-Typ haben.
Prozeduren können an einen Datentypen gebunden sein.

Prozeduren können an einen Typ gebunden sein. Typ-gebundene Prozeduren sind automatisch mit jeder neuen Instanz dieses Typs verbunden.
ForwardDecl  =   PROCEDURE "^" [Receiver] IdentDef [FormalPars].
Beispiel:  PROCEDURE ^ Later;
FormalPars   =   "(" [FPSection {";" FPSection}] ")" [":" Qualident].
FPSection   =   [VAR] ident {"," ident} ":" Type.Prozeduren und Funktionen werden durch PROCEDURE eingeleitet. Funktionen sind durch den mitgegebenen Resultat-Typ gekennzeichnet.
Das Ergebnis einer Funktionsprozedur wird explizit durch eine RETURN-Anweisung übergeben.
Beispiel:  (ein schlechtes Beispiel - die Zwischenrechnung kann zu einem Überlauf führen)
  PROCEDURE Norm(x,y:REAL): REAL:
  BEGIN
    RETURN Math.Sqrt(x*x+y*y)
  END Norm;
Konventionell haben Funktionsprozeduren immer eine (evtl. leere) Parameterliste.
Receiver  =   "(" [VAR] ident ":" ident ")".
Type   =   Qualident
  |   ARRAY [ConstExpr {"," ConstExpr}] OF Type
  |   RECORD ["("Qualident")"]
      FieldList {";" FieldList}
      END
  |   POINTER TO Type
  |   PROCEDURE [FormalPars].
In Oberon können ARRAY-Parameter, in Oberon-2 auch ARRAY-Variable unbestimmter Länge sein. In diesem Fall wird kein Ausdruck für die Länge angegeben. Bei einer Allokierung mit NEW kann die Länge nachgetragen werden; mit LEN kann die allokierte Länge erfragt werden.
Beispiel:
  VAR vec: POINTER TO ARRAY OF REAL;
  ...
  NEW(vec,neededlen);
  ...
  IF LEN(vec)< 100 THEN... END;
Pointer-Typen benutzen POINTER TO.

Records sind erweiterbar. Eine erweiterter RECORD-Typ erbt alle Felder seines Basis-Typs, und deklariert möglicherweise weitere Felder. Für Variable ist die Zugehörigkeit zu einem RECORD-Typ mit IS abprüfbar.
  FieldList   =   [IdentList ":" Type].
StatementSeq  =   Statement {";" Statement}.
Statement   =  [ Designator ":=" Expr
  |   Designator ["(" [ExprList] ")"]
  |   IF Expr THEN StatementSeq {ELSIF Expr THEN StatementSeq} [ELSE StatementSeq] END
  |   CASE Expr OF Case {"|" Case} [ELSE StatementSeq] END
  |   WHILE Expr DO StatementSeq END
  |   REPEAT StatementSeq UNTIL Expr
  |   FOR ident ":=" Expr TO Expr [BY ConstExpr] DO StatementSeq END
  |   LOOP StatementSeq END
  |   WITH Guard DO
    StatementSeq
      {"|" Guard DO StatementSeq}
      [ELSE StatementSeq] END
  |   EXIT
  |   RETURN [Expr]
     ].
    
WITH ist eine Variante von CASE, die nach Typ (nicht nach Wert) selektiert. WITH konvertiert implizit: wenn eine Guard-Bedingung erfüllt ist, so wird die Variable für die zugehörige Anweisungssequenz dem jeweiligen Typen entsprechend behandelt.

CASE-Fälle werden durch | getrennt. CASE kann einen ELSE-Zweig haben.

FOR kann eine Schrittweite haben. Ein spezielles DOWNTO entfällt - dieser Fall wird durch eine FOR-Anweisung mit Schrittweite -1 realisiert
  FOR i=top TO bottom BY -1 DO
  ...
  END

Case   =   [CaseLabels {"," CaseLabels} ":" StatementSeq].
CaseLabels   =   ConstExpr [".." ConstExpr].
Guard  =   Qualident ":" Qualident.
ConstExpr  =   Expr.
Expr   =   SimpleExpr [Relation SimpleExpr].
SimpleExpr  =   ["+" | "-"] Term {AddOp Term}.
Term   =   Factor {MulOp Factor}.
Factor   =   Designator ["(" [ExprList] ")"] | number | character | string | NIL | Set | "(" Expr ")" | " ~ " Factor.Die Negation ist mit ~gekennzeichnet.

Set  =   "{" [Element {"," Element}] "}".Die Mengenklammern sind {, }.
Element   =   Expr [".." Expr].
Relation   =   "=" | "#" | "<" | "<=" | ">" | ">=" | IN | IS.
Für Ungleich steht #.
Die Vergleichsoperatoren gelten auch für Variablen vom Typ ARRAY ... OF CHAR.
IS prüft die Typ-Zugehörigkeit, zum Beispiel IF x IS vector THEN..END;
  AddOp   =   "+" | "-" | OR.
  Die Division für Mengen ist die symmetrische Differenz.

MulOp   =   " * " | "/" | DIV | MOD | "&".
& steht für "und".

Designator   =   Qualident {"." ident | "[" ExprList "]" | " ^ " | "(" Qualident ")"}.
Die Dereferenzierung ist optional, wenn sie aus dem Zusammenhang eindeutig ist.
Z.B.
  A[i,j]  anstelle A^[i,j]
  xyz.abc  anstelle xyz^.abc
Referenzen auf ARRAYs werden iterativ aufgelöst, d. h. A[i,j] ist gleichwertig mit A[i][j].
ExprList   =   Expr {"," Expr}.
IdentList   =   IdentDef {"," IdentDef}.
Qualident   =   [ident "."] ident.
IdentDef   =   ident [" * " | "-"].




Einführung in die Programmiersprache Oberon. Kurs/KapPas.Text
gs (c) G. Sawitzki, StatLab Heidelberg
<http://statlab.uni-heidelberg.de/projects/oberon/kurs/>

Home Up Intro Contents Chapter 1 2 3 4 5 6 7 8 9 10 Design Assert Timing EBNF Report Pas