Home
Up
Intro
Contents
Chapter
1
2
3
4
5
6
7
8
9
10
Design
Assert
Timing
EBNF
Report
Pas
Last Changed: July 12th, 1997
This is a conversion from Oberon text to HTML, and from German to English. 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 for download using binary ftp as Oberon System 3 archive.
The converter from German to English is still under development as well.
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 available
in German as well.
Introduction to Oberon
The Oberon Programming Language
&P: Oberon for Pascal Programmers
This chapter is a quick introduction to Oberon for Pascal or Modula programmers.
Only some short explanations are given here. Pascal or Modula programmers
surely find their own way through the Oberon course
<"http://statlab.uni heidelberg.de/projects/oberon/intro/">.
This section is to point out the special aspects, which else must be looked
for in the bulk of the material.
Oberon belongs to a family of programming languages, with older members of
the family being ALGOL, Pascal and Modula. We concentrate here on the current
version, Oberon-2. The most important characteristics of Oberon-2 are block
structure, modularity, separate compilability, static data types with strong
type checking (even across module boundaries), and extensible data types
with type-bound procedures. Type-bound procedures are a special feature of
Oberon-2. The other features are available beginning with the original Oberon
version.
Oberon is also the name of an operating system (written in the language Oberon).
In this section we concentrate on the Oberon language.
An article of N. Wirth describing the differences between Modula 2 and Oberon
is in <ftp://statlab.uni-heidelberg.de/pub/mirrors/inf.ethz.ch/oberon/docu/>
A Modula->Oberon converter by N. Wirth is available as well.
Pascal programs can be converted to a large extend automatically using
Mess.Style * \i=P \o=O ~
if both Mess und MessP are installed in an Oberon system. Mess is in
<http://statlab.uni-heidelberg.de/projects/oberon/util/>.
We take a rather informal point of view regarding Pascal. Pascal is formally
defined in the Pascal report, complemented by the Object Pascal report, which
extends the language by constructs for object-oriented programming. In practice
there is however a series of deviating implementations, up to peculiarnesses
of individual compilers such as Turbo Pascal, so that a rather pragmatic
approach seems advisable.
Small differences
Compared with Modula and Pascal Oberon is cleaned up. Rarely used language
elements or elements which can be implemented only with difficulty are removed.
Language elements, which were a frequent source of error, have been reduced.
Language elements which made the compiling process lengthy without bringing
a recognizable advantage, have been deleted.
Some of the clean up results are:
- Names are case sensitive. All reserved words use capital letters only.
Example:
BEGIN reserved
begin different
from BEGIN. user defined name
- compound statements always have a statement sequence. The alternative
"statement/statement sequence" has been removed. The opening "BEGIN" has
been removed now: the begin of the statement sequence is already marked by
the compound statement.
Example:
IF i>0 THEN
dothis;
dothat
END;
resp.
IF i>0 THEN
dothis
END;
An ELSIF is introduce to support IF cascades.
Example:
IF i>0 THEN
dothis
ELSIF i=0 THEN
dothat
ELSE
dosomethingcompletelydifferent
END;
- All arrays are 0 based. For the declaration the length is indicated (not
the highest index value).
Example:
VAR a: ARRAY 10 OF
INTEGER;
contains a[0]...a[9].
- logical operations are processed from left to the right. Evaluation is
stopped as soon as the result is established (short cut evaluation).
Example:
IF NoDiskError &
EraseOk THEN ... END;
calls EraseOk only if NoDiskError
returns the result TRUE.
- Procedure blocks are different from compound statements. Procedure blocks
end with an identifier which permits a synchronization with the heading.
Example:
PROCEDURE xyz;
BEGIN
...
END xyz;
- GOTO has been removed. To exit from a loop, EXIT can be used. To leave
a program, HALT is available.
- GOTO does not give it any longer. Over of a loop to break out there is
EXIT; over of a program to break out there is STOP.
Types are extensible. The type system is defined in such a way that its consistency
is to be checked easily and an effective implementation is possible.
- enumerated types have been removed.
- subset types for integer numbers
have been removed.
- PACKED has been removed. Instead
the standard types
CHAR or SHORTINT
can be used for economic storage.
- SET is always a subset of the
natural numbers;
the max. scope is
implementation specific.
Typical ranges are 0..31 or
0..63.
- the rules for type compatibility
have been cleaned up.
- RECORD variants have been removed.
Instead of this
all RECORD types
are now extensible.
The syntax of procedures is unified. There are no special cases for in- or
output on language level. In- or output procedures are services, which are
furnished by "library procedures". However this implies that in- or output
procedures are implementation dependent and not uniform.
Large differences
Separate compilability is an outstanding special feature of Oberon. The program
organization is basically different from Pascal. In Pascal the "main program"
lost already its meaning to a large extent and is often reduced to a trunk.
Cleared up Pascal programs frequently have the structure
PROGRAM xxx;
...
VAR abc: xxxType;
PROCEDURE Init;
BEGIN
...
END;
PROCEDURE DoTheJob;
BEGIN
...
END;
PROCEDURE Exit;
BEGIN
...
END;
...
BEGIN
Init;
DoTheJob;
Exit
END.
In Oberon is this structure reduced to
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 modules can be given exactly the same structure as Pascal programs.
The separate compilation gives additional possibilities. These become clear
in relationship with the Oberon system. In conventional systems programs
are volatile, i.e. after end of the program all program information is lost,
if it is not as printed out, stored or saved otherwise. In Oberon a module
takes the place of a program. The information of a module is preserved, until
the module will explicitly unload.
As consequence several modules can be present at the same time in Oberon.
Modules can access the available information mutually. In order to differentiate
between information from different modules, the name system extended. Oberon
names can be simple designators (as in Pascal), or qualified names. Qualified
names have the form
< module name>.<variable
name >.
Access control is on module level. A * mark following a name on global level
marks the information as public. A - mark indicates readability, but no public
right to change.
The variable ABC for example can be addressed in the module xxx as ABC as
in Pascal. In Oberon the variable ABC can be addressed however also by every
other at the same time active module, if it is exported. Here the qualified
name is used, i.e. other modules use ABC in the qualified form as xxx.abc.
abc:=kiki within
xxx
xxx.abc:=kiki outside
of xxx
The consequences for the programmer are:
- it is not necessary to write
data to an intermediate in order to exchange it with different "programs".
- there is no overhead when dividing
programs into components which restrict to special functions.
Apart from this fundamental difference, the parting from the global program
in favor of module components, there are two further modifications which
make Oberon an object-oriented programming language: RECORDs are extensible
and procedures are first class members of the type family.
RECORDs are extensible: RECORD data types form a heritability hierarchy.
Derived data types inherit all fields of their ancestors, and can have additional
data fields. Declarations for derived data types have the form
NewRecord=RECORD (oldrecord)
NewField: NewFieldTyp
...
END;
Procedure types are on one level with other types. In particular procedures
can be transferred not only as parameters, but also be stored in variables.
It is also possible to encapsulate procedures as methods with the appropriate
data in a RECORD type i.e. object-oriented programming is supported.
Example:
Handler*=PROCEDURE(obj: Object;
VAR M: ObjMsg);
ObjMsg*=RECORD
...
END;
Object*=POINTER TO ObjDesc;
ObjDesc*=RECORD
...
handle*: Handler
...
END;
Garbage
On procedure level the block structure of Oberon, like with all languages
of the Oberon family, ensures that the storage space is managed correctly.
However If storage space is requested explicitly under program control, for
example with NEW, a reference to the storage space can be passed on as parameters,
value of variables or in other ways, without the control of the operating
system. The usual way was to leave the memory management to the programmer.
The programmer would allocate memory with procedures like NEW, and hopefully
would release it again with procedures such as DISPOSE. Even with classical
programs this could be a source of problems. The programmer could try to
use memory space without allocating it first. This situation is usually recognizable,
and it could be left to compiler builders to find ways to avoid this error.
The other possible errors are releasing memory although it is still used,
or not releasing memory although it is not used anymore. The first problem
leads to a problem well-known as "dangling pointers" , the second problem
to "memory leakage".
In Oberon these problems are avoided. "garbage collection" is done by the
Oberon system. There is a procedure "NEW" like in Pascal, but there is no
"DISPOSE". The system is responsible for releasing memory. The system has
to prove that memory to be released cannot be accessed any more.
The programmer can ease the task of finding unaccessible memory by setting
dynamic variables to NIL as soon as they are not needed any more.
There is a weak point in some older Oberon implementations. Oberon allows
to release a modul explicitly. But a module can contain procedures, which
may be referenced to by variables. While immediate references to procedures
are handled correctly in nearly all Oberon implementations, some may have
difficulties if a a procedure reference is stored in a variable. Oberon allows
to mark procedures which are passed in variables using a * after the reserved
word PROCEDURE. This possibility should be used as a hint for compilers to
avoid possible difficulties.
Pre-defined procedures
A small group of procedures plays a special role. These procedures are defined
in the Oberon report (section 10.3) and implemented in each Oberon system.
Partially the type compatibility conditions for these procedures are relaxed.
In comparison with PASCAL there are some replacements:
- PRED and SUCC are replaced by the more general procedures DEC and INC.
- ROUND has been removed. ENTIER is used to convert from real numbers to
integers.
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 systems and libraries
In Oberon there is no fundamental difference between system, libraries
and user programs. Each of these is implemented in modules, and each module
can access services of any other modules. Access rights are checked on module
level. Each module specifies which information is exported. Only information
(constant, type definitions, variable, procedures) on the highest (global)
level can be exported. The export conditions are specified using a mark in
the declaration after the name. Names marked "*" are generally exported;
names marked "-" are exported only for reading.
An importing module specifies all modules to be imported in an import declaration.
An import declaration has the form
IMPORT <name>[ , <name>]
;
"Alias" names can be introduced. For an alias, < name > has the form
<alias-name>:=<orig. name>
The available modules are implementation specific. The most important implementations
are (as of July 10th, 1998)
Oberon V4 the
current implementation of the
original Oberon
system.
Available on
practically all architectures.
Oberon System 3 an
earlier variant of Oberon.
A major part of the
Oberon development
has concentrated
on System 3, so that
most important new
developments
take place in System
3.
Available for MS-DOS,
LINUX,
Windows, Mac.
Various local differences
to Oberon V4,
numerous extensions.
Oberon/F an
Oberon based framework
with numerous extensions.
Available on Windows
and Mac only.
Largely incompatible
with Oberon V4
or System 3. Now
replaced by BlackBox.
BlackBox a
commercial framework using
Component Pascal,
a proprietary dialect
based on Oberon
Available on Windows
and Mac only.
Largely incompatible
with Oberon V4
or System 3.
Each implementation supplies its
own libraries, which unfortunately do not always coincide even for the basic
procedures. Therefore the user depends on which special implementation is
installed.
For input and output there are simple models, which are presented in the
book by Reiser&Wirth. Practically each implementation offers acces to
these models. They can be used, if the following modules are imported:
In Input
Out Output
XYplane graphical
output.
Depending on the implementation, an initialization is needed before using
these modules.
In.Open (resp.
Out.Open, XYPlot.Open).
Usual services are:
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.
The advanced programmer will avoid these modules. They are educational examples,
which are replaced by more flexible and more efficient models available for
professional work. Oberon V4 and S3 supply basic elements in the modules
Texts and Display (or Display3) for text and graphical in/output.
Details
We give here a commentated formal definition of Oberon in extended Backus-Naur
form. The original definition is an appendix of the Oberon report.
The elementary items are:
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".
Numerical types are
SHORTINT INTEGER LONGINT REAL
LONGREAL.
1.4E3 denotes a REAL constant, 1.4D3 a LONGREAL constant. The conversion
from a lower numerical type to a higher one is done automatically. Conversion
from a higher (longer) data type to a lower one is done by the SHORT procedure
To go from a real type to an integer type, an explicit conversion using the
procedure ENTIER is needed.
Warning: The higher data types cover the scope of the lower. However it is
not guaranteed that the accuracy covers those of the lower data type. In
particular generally the accuracy is lower with REAL numbers than with LONGINT.
Hexadecimal numbers can be written in H-notation, for example 0FFH.
character const = digit {hexDigit} "X" | '"' character '"'..
string = ' " ' {char} ' " ' | " ' " {char} " ' ".
Character constants can be given in hexadecimals (for example 0X, 0DX) or
as characters (for example "A"). String constants can contain " or ', but
not both.
By convention, string variables are stored as ARRAY OF CHAR with a terminating
character 0X.
Comments are delimited by (*... *) and can be nested.
Module = MODULE
ident ";" [ImportList] DeclSeq [BEGIN StatementSeq] END ident ".".
The MODULE takes the place of the program. Modules are resident and can make
information mutually accessible.
ImportList = IMPORT
[ident ":="] ident {"," [ident ":="] ident} ";".
Module can import other modules, as long as no "import cycles" are introduced.
Imported modules must be declared in an import declaration. Upon import,
alias names can be introduced for modules, for example 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.
Procedure declarations end with an END, followed by the procedure name. There
is no special type for function procedures. Function procedures are special
cases of procedures, having a result type.
Procedures can be bound to a type. Type-bound procedures are automatically
associated with each new instance of this type.
ForwardDecl = PROCEDURE
"^" [Receiver] IdentDef [FormalPars].
Example: PROCEDURE ^ Later;
FormalPars = "("
[FPSection {";" FPSection}] ")" [":" Qualident].
FPSection = [VAR]
ident {"," ident} ":" Type.Procedure declarations begin by PROCEDURE. Function
procedures are marked by a result type.
The result of a function procedure is returned explicitly, using a RETURN
statement.
Example: (a bad example - the intermediate
calculation can lead to an overflow)
PROCEDURE Norm(x,y:REAL): REAL:
BEGIN
RETURN Math.Sqrt(x*x+y*y)
END Norm;
By convention function procedures always have a (possibly empty) parameter
list.
Receiver =
"(" [VAR] ident ":" ident ")".
Type = Qualident
|
ARRAY [ConstExpr {"," ConstExpr}] OF Type
|
RECORD ["("Qualident")"]
FieldList
{";" FieldList}
END
|
POINTER TO Type
|
PROCEDURE [FormalPars].
In Oberon ARRAY parameters can be of indefined length (open arrays). Oberon-2
allows open arrays for types and variables as well In this case no expression
for the length is given. Upon allocation with NEW the length needs to specified.
Using with LEN, the allocated length can be queried.
Example:
VAR vec: POINTER TO ARRAY OF REAL;
...
NEW(vec,neededlen);
...
IF LEN(vec)< 100 THEN... END;
Pointer types use POINTER TO.
Records are extensible. An extended RECORD type inherits all fields of its
base type, and possibly defines additional fields. For variables membership
to a RECORD type can be checked using IS.
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 is a variant of CASE, which selects according to type (not by value).
WITH converts implicitly: if a guard condition is fulfilled, then the variable
is treated as variable ot that type for the corresponding statement sequence.
CASE entries are separated by |. CASE can have an ELSE branch.
FOR can have a step size. There is no special DOWNTO - this is implemented
by a for statement with step size -1.
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.The negation operator is ~.
Set =
"{" [Element {"," Element}] "}".Set braces are {, }.
Element = Expr
[".." Expr].
Relation = "="
| "#" | "<" | "<=" | ">" | ">=" | IN | IS.
The not equal operator is #.
The comparison operators apply also to variables of the type ARRAY ... OF
CHAR.
IS check type membership, for example IF x IS vector THEN..END;
AddOp =
"+" | "-" | OR.
Division for sets is the symmetric
difference.
MulOp = "
* " | "/" | DIV | MOD | "&".
& means "and".
Designator = Qualident
{"." ident | "[" ExprList "]" | " ^ " | "(" Qualident ")"}.
Dereferencing is option if it is well-defined by the context.
for example.
A[i,j] instead
of A^[i,j]
xyz.abc instead
of xyz^.abc
References to array ARRAYs are resolved step b step, i.e.. A[i,j] is equivalent
to A[i][j].
ExprList = Expr
{"," Expr}.
IdentList = IdentDef
{"," IdentDef}.
Qualident = [ident
"."] ident.
IdentDef = ident
[" * " | "-"].
Introduction to the Oberon programming language. ItO/ChPas.Text
gs (c) G. Sawitzki, StatLab Heidelberg
<http://statlab.uni-heidelberg.de/projects/oberon/intro/>
Home
Up
Intro
Contents
Chapter
1
2
3
4
5
6
7
8
9
10
Design
Assert
Timing
EBNF
Report
Pas