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
05 Declarations; Types; Text Output
All data which are processed need an internal representation in a computer.
From a technical point of view, the computer memory is homogeneous. From
this point of view, all data are equivalent. For the user however different
data have different interpretations: we have logical values, integer numbers,
real numbers, characters, character sequences and many other types which
need to be represented. Hence typically three aspects are related by a declaration:
a memory location, where the data can be stored; a name, used to refer to
this memory location and the data stored there; and a type which specifies
an adequate interpretation. Besides variable declarations, Oberon allows
type declarations to associate a name with a (possibly abstract) type.
In Oberon, each name has to be introduced by a declaration before it is used.
Some names are predefined. These have an implicite definition and need not
be declared before they are used. The first group of names denotes basic
data types:
1. BOOLEAN
logical values true
(TRUE) and false (FALSE)
2. CHAR
characters from the
extended ASCII character set (0X .. 0FFX)
3. SHORTINT
integer numbers from
MIN(SHORTINT) to MAX(SHORTINT)
4. INTEGER
integer numbers from
MIN(INTEGER) to MAX(INTEGER)
5. LONGINT
integer numbers from
MIN(LONGINT) to MAX(LONGINT)
6. REAL
real numbers from
MIN(REAL) to MAX(REAL)
7. LONGREAL
real numbers from
MIN(LONGREAL) to MAX(LONGREAL)
8. SET
sets of integer numbers
from 0 to MAX(set)
The types 3 through 5 are integer number types, types 6 and 7 are real types.
The constants TRUE and FALSE are pre-declared, as well as the procedures
MIN and MAX. For a basic type T, MIN(T) is the minimal value and MAX(T) is
the maximal value which can be assigned to a variable of type T.
"Real numbers" is a simplified term only. All numbers in a computer have
a finite representation. What is called "real numbers" here is but a very
restricted subset of the rational numbers. The specific range of number
representations is not defined in Oberon, but is implementation specific.
Variables are introduced by declaring name and type, using this form:Variable
declaration:
name
list :type name
For example
i, j, k: INTEGER x,
y: REAL p, q: BOOLEAN
s: set F:
Function
Generally all elements in Oberon need to be declared before they are used.
There is an exception for procedures: it is possible to give a reference
to a procedure which will be defined later on. After that, the procedure
can be used before the proper definition is given. This "forward declaration"
has the syntax
Forward declaration:
PROCEDURE
^ [target] procedure name [formal parameters];
The full syntax of the declaration sequence is:
Declaration sequence:
{ CONST
{constant declaration ; }
| TYPE
{type declaration ; }
| VAR
{variablen declaration ; }}
{ procedure declaration
;
| forward declaration;
}
where
Constant declaration:
name
definition = constant expression
Type declaration:
name
definition = type
Variable declaration:
namen
list : type
and
Name list:
name
definition { , name definition }
Examples:
CONST Pi=3.14159;
K=1024;
TYPE AgeType=
SHORTINT;
VAR Age:AgeType;
Len: INTEGER;
Each declaration has a scope of validity, and these scopes build a hierarchy.
A validity scope starts at the declaration and ends at the end of the containing
segment (module or procedure). Declarations within a procedure are valid
upto the end this procedure. The scopes are nested. Each name may be declared
only once in each scope. If a name is used in a procedure, which has already
been declared on a higher level, the previous declaration becomes invisible
until the end of the procedure, and only the local declaration is valid within
the procedure scope.
Constant declarations are used in order to define symbolic names for better
legibility or for abbreviation. The constant expressions in the declarations
are evaluated at compile time. This should be kept in the mind if compiling
and execution do not take place on comparable systems.
Variable declarations reserve storage space for variables. At the same time
they determine the type conventions according to which the information is
to be interpreted. A value which is compatible to a type t can be assigned
to a variable of type t by an assignment statement. The syntax of assignments
is:
Assignment:
name
:= expression
The elementary data types are pre-defined. Beyond this, Oberon offers
the possibility of defining more complex data types. We illustrate this using
an example from the Oberon system itself: the text output to a text frame.
If there were only one window, only one framework and only one current process,
we could append an output text to the most recent output, character by character.
The situation becomes more complicated if there are several sources for the
data stream. Already in classical systems an error condition might occur
while some results are printed out. There may be an urgent message to alert
the user while the results still need output. Classical systems use two logical
output streams, a standard output, and an error output (diagnostic, stderr...)
where an error message may interrupt the standard output. If there are not
only error messages, but a multiplicity of sources from which information
can arise, this protocol becomes unpractical. It needs to be guaranteed that
information from different sources is not mixed, but remains consistent.
The Oberon solution is that each output channel decides for itself when a
message is complete and may be output. To allow for this, the information
is buffered. It is appended to an output stream only when the information
is complete. Besides the actual information, the output characters, some
additional information must be noted, for example the character set to be
used, the color, vertical shift etc.. All this information needs to be preserved,
even if output from other sources is intermittend. In Oberon, these information
aspects can be collected in a data type Writer,
which is defined exactly to hold this kind of information.
Some of these information constituents, as for instance the vertical shift,
can be represented by numbers. Others, for instance the buffer, are more
complex. To use a buffer in a controlled way, administrative information,
like buffer size and present/immediate allocation of the buffer, must be
available. Buffers are not only used as intermediate storage for output,
but also at various other places. The Oberon strategy is it to define a general
type for the buffer which is re-used for writing
in the Writer type.
All together these declarations have the form
TYPE
Buffer* = POINTER TO BufDesc;
BufDesc* = RECORD
len*: LONGINT;
...
END;
Writer* = RECORD
...
buf*: Buffer;
...
col*: SHORTINT; (*
color (index in a color table) *)
voff*: SHORTINT;
(* vertical offset *)
END;
These declarations are contained in the standard module Texts. We
return to these definitions later now. For now we note:
Complex, composed data types can
be defined in Oberon.
User defined data types can be used as components in further declarations.
Another example of a complex data type is the type Text. It represents a
complete text, with contents and all layout attributes important for a text.
The data type Text is defined in the module named Texts, together with the
data types Buffer and Writer.
There is one variable of type text which is always available for output by
convention. It is defined in the module Oberon. Since the the Text data
is not defined in the module Oberon, but is imported from the the Texts module
it must be referenced in the qualified form as Texts.Text. In module Oberon,
the definition of the "standard output text" is
VAR
Log* :
Texts.Text;
Contents of a buffer buf is appended to this text using
Texts.Append(Oberon.Log,buf);
How do we prepare information in the buffer of a Writer? First the Writer
needs to be declared. This is done by a declaration of this form:
VAR
W :
Texts.Writer;
Next it has to be made sure that the buffer is written properly, starting
at its first position. The necessary initialization is achieved by a statement:
Texts.OpenWriter(W);
After this, the Writer is set up and prepared to receive information.
For output, we want to control the format as well as the information contents.
Numbers and texts can be output in a multiplicity of formats. In order to
control these formats, a whole collection of functions is available. Two
of these are for example:
Texts.WriteString(W,"Hello"); appends
"Hello"
Texts.WriteLn(W); moves
to a new line.
The example given above needs some modification now. We do not want
to send output to an arbitrary buffer, but to the buffer of our writer W.
The correct statement is:
Texts.Append(Oberon.Log,W.buf);
The data structure Texts.Writer is a complex data structure built
from several components. W.buf is the buffer component of this data structure
and has the type Texts.Buffer.
The current contents of the text Oberon.Log can be displayed using module
System. System.OpenLog opens a window with the name
System.Log, and displays the text Oberon.Log. System.ClearLog
deletes the contents.
A complete module is opened with
Desktops.OpenDoc
ItO/ItOProg06.Mod
To compile it, use
Builder.Compile
ItO/ItOProg06.Mod \s ~
Test it using
ItOProg06.Hello
A complete list of the the types, variables and procedures exported by Texts
is generated by
Browser.ShowDef
Texts ~
or
Watson.ShowDef
Texts ~
The output routines have names, which begin with Write.... Apart from
output device and the value to be sent to output, most routines contain further
parameters, which determine the format a (usually: n: Number of places which
can be output, if necessary k: number of post-decimal positions). You find
a description of the routines in Wirth/Gutknecht: Project Oberon.
Exercises:
Get a copy of the sample module using
System.CopyFiles ItO/ItOProg06.Mod
=> ItO/ItOTest06.Mod
~
Open this copy. Change the name of ItOProg06 to ItOTest06;
compile the module and make sure the module still works by executing
ItOTest06.Hello
Add a procedure
Procedure Range*;
BEGIN
Text.WriteString(w," Integer MIN:
");
Text.WriteInt(w,MIN(INTEGER),20);
Text.WriteString(w," Integer
MAX: ");
Text.WriteInt(w,MAX(INTEGER),20);
Text.WriteLn(w);
Text.Append(Oberon.Log,w.buf);
END Range;
Compile and test again, using
ItOTest06.Range
Complete the procedure Range to give a table with all
elementary number types, separated into columns:
Type MIN MAX
SHORTINT ...... ......
INTEGER ...... ......
LONGINT ...... ......
REAL ...... ......
LONGREAL ...... ......
Set ...... ......
Note: With proportional fonts (e.g. Syntax) the organization
of tables is difficult. The above table is arranged using a ruler and a tabulator
stop. With Texts.Write(W,09X) you can insert the ASCII character 9 = tab.
Another possibility is it to use a font with fixed character width (e.g.
Math or Courier) for the output text .
Apart from the output field width, for real number types REAL and
LONGREAL the accuracy the output is of importance. Depending upon the implementation
the accuracy of the internal number representation does not have to be homogeneous.
A finer accuracy is usual used with small numbers than with large. In Oberon
there is no pre-defined possibility to query the accuracy. It must be determined
dynamically, and this is not trivial. A widely used solution is a program
which you can compile using
Builder.Compile ItO/ItOmachar.Mod
~
To start the program, use
ItOmachar.Init
After starting the program, use
System.State ItOmachar.Init ~
or
For the long real version, use
Builder.Compile ItO/ItOmacharL.Mod
~
ItOmacharL.Init
System.State ItOmacharL.Init ~
to get the values determined by the program. The exact meaning
of these values is described by comments in the heading of these modules.
The type of a variable determines which values it can take. It also determines
which operators are applicable to a variable, and how the operators are to
be interpreted.
For real number variables there are the usual operators +, -, *, /. The slash
/ always means a real division.
For the integer division there is the special operator DIV, which is linked
with the modulus MOD: for all integer values x and positive divisors y
x = (x DIV y) * y + (x MOD y)
0 <= (x MOD y) < y
For sets, the operators are sed accordingly:
+ union
- difference
(x - y = x * ( - y))
* intersection
/ symmetric
difference (x / y = (x-y) + (y-x))
The unitary operator - denotes the complement, i.e.
- M = { 0..MAX(SET)}-M.
The logical operators are OR, & , ~ corresponding to or, and, not.
meaning
p OR q means if
p then TRUE else q end;
p & q means if
p then to q else FALSE end;
In more detail: If p is true, then q is not even analysed and (p OR q) is
true . If p is not true, then q is analysed and the output has the respective
value of q. The same applies to &. OR and & thus are not not commutative.
If p and q are elementary expression, this is of no relevance. But p and
q can represent more complex operations, which have a side effects during
the analysis of their logical value. For example imagine q is a procedure,
which deletes and formats a hard disk, returning some error code, and p may
be the result of a user confirmation as to whether the deletion is desired.
The analysis
IF p THEN q ELSE FALSE
of p&q probably yields the required result, i.e. tells whether everything
ran as required. Every other sequence of analysis presumably is not the desired
result.
For relations, the operators
= equal
# unequal
< less
<= less
or equal
> larger
>= larger
or equal
IN element
of
are available.
Type definitions of complex types can be extended. The special operator
IS is
of the type
serves to check the type affiliation of a variable dynamically.
Name
IS Type
So for example
x IS y
for a variable x and a type y tells whether x belongs to type
y.
Introduction to the Oberon programming language. ItO/Ch05.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