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.
&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