The Oakwood Guidelines for Oberon-2 Compiler Developers

Description
Initial developments of the Oberon-2 language were done on the Oberon OS itself, but when people started to develop Oberon-2 compilers for other system they ran into problems since the Oberon system is quite alien to most other systems on the market and either lacks common features found on them or they work very differently from the norm, on top of that the Oberon-2 compiler writers could take advantage of subsystems that do not exists on other operating systems.

To aid those porting the Oberon-2 compilers, Hanspeter Mössenböck and Josef Templ of ETH alongside Brian Kirk of British developer Robinson Associates put together a this document below that is referred to a "The Oakwood Guidelines for Oberon-2 Compiler Developers" that specifies what basic libraries there should be in an Oberon-2 development system and how they should behave. This was the result of a conference held in Oakwood, Croydon, UK in 1993 where practitioners from all around the world came together do discuss the problem.

Note that these are guidelines rather than a formal standard, but it is adhered to by most if not all modern Oberon-2 compilers. Also note that this is an historical document, all email addresses, ftp and other Internet locations, telephone and FAX numbers and physical & postal addresses are bound to have changed, standards and documents referred to may have changed or been deleted, it is even not possible to assume that persons and organisations mentioned in the text still exist.

Disclaimer
Note that the standards text below is here for reference purposes and unlike other material on the EDM/2 Wiki is not under the Attribution-ShareAlike 3.0 license and should be treated as if it is owned by Robinson Associates. As it is a standard the text below it should not be modified except for formatting purposes, if a more recent version is published a new page should be created for that standard since software packages that do not get updated will continue to be conform to the older standard.

SUMMARY
Title: The Oakwood Guidelines for Oberon-2 Compiler Developers.

Scope: This document is a companion document to the ETH Oberon-2 Report and contains clarifications, extensions, implementation recommendations and a basic library definition.

Purpose: To document the discussions held at the Oakwood Conference in Croydon 1993 and to provide practical guidance for compiler writers. The objective being to have a common approach to Oberon-2 compiler implementations over a wide variety of platforms and to encourage consistency wherever practical.

Authors: See Revision: 1A First Issue Edited by: Brian Kirk, Robinson Associates

Dedication
This document is dedicated to the memory of Nick Walsh.

His sound advice and subtle wit combined with intellectual clarity will be sorely missed by friends, colleagues and students.

Preface
The Oberon language - together with the Oberon System - was designed and implemented by Prof. Niklaus Wirth and Prof. Jürg Gutknecht at ETH Zürich from 1985 to 1987. Since then it has been used intensively for teaching but also for projects at ETH and elsewhere. After some minor changes - which also led to Oberon-2 - the language finally became stable and mature. Currently, it is available on practically all modern platforms. All these implementations support the same language and even the same interfaces to files, windows and other operating system resources. One could thus speak of a de facto standard.

This was the situation when a group of about 30 compiler developers and vendors met at the Oakwood Hotel in Croydon in June 1993 to agree on a common set of language features and library modules that should be provided by every Oberon-2 system. This group worked in a very efficient way avoiding bureaucracy and lengthy meetings. Within a few months they produced a document which subsumes the results of the Oakwood meeting and establishes a set of guidelines to compiler developers.

Beside some clarifications to the language report and a modest set of possible extensions, the central part of this document gives hints to compiler developers and defines a basic module library that should come with every Oberon-2 implementation.

I hope that future developers of Oberon-2 systems will stick to these guidelines for the benefit of uniformity and portability. Oberon-2 will only be successful if it does not repeat the mistakes of Modula-2 implementations where a lack of agreement between early compiler developers led to incompatibilities and an all too lengthy standardisation process.

My special thanks go to Brian Kirk and Euan Hill who acted as conveners of the Oakwood meeting and later undertook the difficult task of collecting and assembling all the comments, suggestions and wishes into a consistent and reasonably short document. Thanks also to all individuals listed at the end of this document who contributed in a spirit of cooperation.

Hanspeter Mössenböck ETH, Zürich November, 1993

Notes from the Editor
Here is the first issue of the Oakwood Guidelines. It is based on the draft circulated by Email which has been amended based on your feedback and reviewed with Prof. Mössenböck and Josef Templ at ETH. I am aware that possibly all the contributors may possibly be disappointed! The reason is simple, there have been many ideas put forward and I have tried to include only items which were discussed at the Oakwood meeting or have really strong support. Items in brackets < > highlight topics that require further clarification. Any errors in the draft are likely to be mine.

What next? My feeling is that the compiler developers (see ) should try to refine and agree the contents of this document, and not add any more to it, maybe even remove items from it. Above all we should avoid a repeat of the Modula-2 standardisation story as probably nothing useful would be achieved in practice. Your feedback on the content of the draft and possibility for a further meeting would be most welcome.

Brian Kirk Robinson Associates Red Lion House St Mary's Street Painswick GLOS GL6 6QR Voice (+ 44) (0)452 813 699 Fax (+ 44) (0)452 812 912 e-Mail : robinsons@cix.compulink.co.uk.

The Oakwood Guidelines
These guidelines have been produced by a group of Oberon-2 compiler developers, including ETH developers, after a meeting at the Oakwood Hotel in Croydon, UK in June 1993. The purpose of that meeting was to agree on a standard specification for the Oberon-2 Language, some minimal extensions and a standard portable library. The intention is that all implementors should offer support for Oberon-2 to at least the ETH specification standard and also offer an implementation of the basic library modules. The aim is to ensure that Oberon-2 programs using the library will be consistent and portable across all conforming implementations.

The initial motivation behind the Oakwood meeting was to avoid the fate of Modula-2 being repeated with commercial implementations of Oberon-2. Unfortunately Modula-2 implementations introduced many dialects of the language and many incompatible basic libraries. The standardisation process for Modula-2 took far too long and opened the door to a pandoras box of extensions. The objective of this report is to acknowledge the ETH Oberon-2 language report as the base standard and to provide information useful for compiler developers so that compilers and their basic libraries provide a basic level of compatibility.

Oberon-2 Language Standard
The standard specification of the language ETH Oberon-2 is contained in a report which is controlled and published by ETH Zürich. The current version of the report is available by anonymous FTP transfer over INTERNET from the directory :

neptune.inf.ethz.ch:~ ftp/Oberon/Docu

The latest complete version of the ETH Oberon-2 report is in file Oberon2.Report.ps.Z.

A chronological list of all changes made to the report is in file Oberon2.ChangeList.ps.z

Use of the name Oberon
The name Oberon has been trademarked by ETH in the context of the operating system and the language. In order to respect the ETH trademark any compiler that does not at least implement ETH Oberon or Oberon-2 should not be referred to or named as an Oberon or Oberon-2 compiler.

When referring to features of ETH Oberon in documentation it is acceptable to use the terms Oberon or Oberon-2. However when referring to any compiler specific extensions the term Oberon should be qualified with an adjective. For example : "XYZ Oberon-2 supports complex numbers"

In the interest of users, it is strongly recommended that whenever implementors provide a description of their product they specify the extensions that they do or do not support, and the additional libraries they provide. Implementors should state as part of the description of their compilers whether or not extensions are supported in accordance with these Oakwood Guidelines.

Introduction
This chapter consists of a list of language clarifications. Ideally there would be no necessity for clarifications and it is hoped that, where relevant, the ETH Oberon-2 Report will be modified at some time in the future. The clarifications listed here are a snapshot of the situation in September '93.

Status of NIL
NIL is a reserved word denoting a predefined value. In contrast to TRUE and FALSE the type of NIL cannot be expressed in Oberon-2.

Illegal Operations
The following operations are illegal. Their effect is system dependent.
 * 1) De-referencing a NIL pointer.
 * 2) Calling procedure variables with a value NIL.
 * 3) Type tests and type guards with NIL pointers.
 * 4) Indexing an array with an index that is out of range.
 * 5) Accessing a set element outside the range 0 .. MAX (SET).
 * 6) Applying SHORT (...) to an argument with value not in the range of the result type.
 * 7) Operations on strings, or character arrays containing strings, that are not null terminated.
 * 8) Overflows.

WITH and guarded variables
It is possible to alter a guarded pointer variable within the scope of a guarding WITH statement, example: TYPE T = RECORD END;     P  = POINTER TO T;	  T1 = RECORD (T) END; P1 = POINTER TO T1; T2 = RECORD (T) END; P2 = POINTER TO T2; PROCEDURE X;	 VAR p: P; p1: P1; p2: P2; PROCEDURE Y; 	 BEGIN p := p2	 END Y;	BEGIN NEW (p); NEW(p1); NEW(p2); p := p1; WITH p: P1 DO	   Y  (*p is now of type P2 and not P1*) END END X; A practical way to handle this is:

If the compiler can be sure it is safe then give no warning message. If there can be any doubt then do give a warning message. A sophisticated compiler could automatically insert the additional relevant type guard checks.

String Comparison
Strings are always null terminated. Character arrays that are to be compared or used as the source operand of the COPY procedure must contain 0X as a terminator. The comparison a relop b, where a and b are (open) character arrays or strings and relop is =, #, >, >=, <, <= is performed according to the following pseudocode PROCEDURE Compare (a, b: ARRAY OF CHAR; relop:RELATION): BOOLEAN; i := 0; WHILE (a[i] 1 0X) & (a[i]=b[i]) DO	   INC (i) END; RETURN a[i] relop b[i] END Compare

Declarations
The declaration of structured type cannot contain itself. For example a RECORD declaration cannot have itself as the type of one of its fields.

A module must not import itself, for example MODULE x;	 IMPORT x; END x. However the module name can be used for aliasing, for example MODULE x;	 IMPORT x:=y; VAR i: x.INTEGER; END x. This is, however, bad programming style.

String and Character Compatibility
A string of length 1 can be used in any context where a character constant is allowed and vice versa.

Redeclaration of predeclared identifiers
Any predeclared identifier can be redeclared. For example TYPE INTEGER = LONGINT; and PROCEDURE ABS; BEGIN ...	END ABS; Obviously such practice should be discouraged and if used at all used with extreme care.

Truncation of precision
The type inclusion hierarchy may infer an implicit truncation of precision between REAL and LONGINT. For example, if both types are represented in 32 bits then the REAL mantissa precision is likely to be only 24 bits. An assignment from a LONGINT to a REAL will therefore involve a truncation of precision of value assigned.

Introduction
Language extensions are features provided by compiler developers which are in addition to the language as specified in the ETH Oberon-2 Report.

The purpose of this chapter is not to encourage extensions. The reason for defining them here is to promote a uniform approach to the specification and provision of extensions across different compilers and endeavour to make sure that when the same extension is supported by more than one compiler it has the same syntax and semantics in each. If a particular compiler offers a means to optionally support language extensions then the default compilation option is for no extensions to be enabled.

Additional Datatypes
Extending ETH Oberon-2 with new data types is a very contentious issue. At the Oakwood meeting the general feeling was that only the complex number type should be considered. Enumerations and unsigned types have been specifically rejected by ETH although they are still found desirable by applications programmers. Unsigned types are particularly important when interfacing to existing external standard libraries such as X Windows, 'C' or Windows and had support from the applications programmers. Bit level types are considered to be unnecessary as the SET type can be used.

Type inclusion Hierarchies
Adding data types which are additional to Oberon-2 should be done sympathetically (if at all) and with due consideration to the implications on the whole language. Separate type inclusion hierarchies should be used to separate families of types which are intrinsically incompatible. Explicit conversion procedures should be used to convert values that can be represented in different type inclusion hierarchies. The predefined function procedures LONG and SHORT should provide conversion within any extended type hierarchy.

An example (please note this is NOT a proposal for general implementation)

LONGCOMPLEX => REAL => LONGINT => INTEGER => SHORTINT

LONGCARD => CARDINAL => SHORTCARD

LONGCHAR => CHAR

The intention of this scheme is to retain the benefits of type inclusion whilst separating explicitly the system dependent aspects of value conversion between types in different type inclusion hierarchies. Such procedures should be included as built in procedures. For any additional data type extension to the language it is the implementors responsibility to provide an updated version of the Oberon-2 Language Report indicating all the relevant changes required to it.

There is a known problem with this proposal. It does not allow for type inclusion of COMPLEX within LONGREAL. However it was felt to be a better solution than having only one complex type selectable as LONG by compiler switch, which could easily be set to select different options in different modules (and library modules).

<< BK: A proposal is needed for the conversion routine, see Section >>

Type COMPLEX and LONGCOMPLEX
Complex numbers are made up of two parts (real, imaginary). The type LONGCOMPLEX is defined as (LONGREAL, LONGREAL), and can be included at the top end of the type inclusion hierarchy. The type COMPLEX is defined as (REAL, REAL) and is an extension of REAL within the hierarchy. See Section 3.4.1. If a value of a type less than or equal to REAL is interpreted as a COMPLEX value then it is considered to be the real part; the corresponding imaginary part is 0. All expression and assignment compatibility rules can be applied to the complex types, for example VAR c: COMPLEX; r: REAL; i: INTEGER; c:=i+r; c:=c*r;

New conversion functions
The following predeclared function procedures are defined, (z) stands for an expression << BK/HM/AF: Predeclared functions SHORT, LONG, MIN, MAX and SIZE need to be defined for COMPLEX and LONGCOMPLEX. >>

Complex literal number syntax
A common notation is used for complex number literals:

number = integer | real | complex.

complex = real "i".

Examples Values RE	 IM 1.i	           0. 1.	2.+3.i	           2. 3.	4.	           4.	  0.	5.3-6.2i           5.3	-6.2

Reasons against introducing COMPLEX
The omission of COMPLEX data types from Oberon was a deliberate ETH design decision and not an oversight. The following reasons are cited by Josef Templ.

Internal Representation
Cartesian or polar? Both have advantages, Cartesian is more common, though.

Efficiency
Utmost efficiency can only be gained by coding COMPLEX operations as REAL operations, because often the real or imaginary parts are zero, one, or a value which allows algebraic simplifications.

Accuracy
Not under full programmer control in case of COMPLEX.

Difficulties in the hierarchy of numeric types.
The linear type inclusion would be changed to a directed acyclic graph (DAG) if two types COMPLEX and LONGCOMPLEX are introduced with compatibility rules as naturally expected. A simplification would be to set COMPLEX = LONGCOMPLEX, but is it sufficient ? Another simplification would be to form a separate hierarchy consisting of COMPLEX and LONGCOMPLEX, but is this convenient ?

One should also observe the effect on the rest of the language definition. For example, what about the comparison operators for numeric types ?

Implementation
Not the most important point, but COMPLEX also makes the compiler more complex, especially when good code should be generated. The reason is that two separate operand descriptors must be maintained in the compiler to represent the two parts of one complex.

Hardware
Unlike INTEGER and REAL, no hardware support for COMPLEX is available.

Syntax
Additional syntax is necessary for denoting complex constants and/or additional predeclared functions are necessary.

Structured function returns
A common misbelief is that introducing structured function returns would eliminate the discussion about COMPLEX, because then one could define complex operations as functions. It should be noted that this is only half the way since the mathematicians still want to have infix notation which would require the introduction of a more general overloading concept including infix operators. This in turn would break the idea of always qualifying imported objects by the module name.

Unused
For the reasons outlined above (efficiency, accuracy), many Fortran programmers don't use complex operations although they are supported by the language.

Not sufficient
For the purpose of scientific computing, COMPLEX is only a small step. What is still missing are vector operations and subarrays.

Interrupt and Code Procedures
A consistent means of providing a clean Oberon-2 interface to highly system dependent features is defined by encapsulating such features in procedures, which are inherently unsafe.

Interrupts are implemented by marking the procedure with + as a prefix

PROCEDURE +Proc ... ; ... END Proc;

At run-time the procedure has to be associated with the required interrupt using an installation mechanism such as

Install (Proc, number);

Where number represents a position in a vector table or an actual vector address location, clearly this is implementation specific. Code procedures are implemented by marking the procedure with a - as a prefix.

PROCEDURE -ProcHeading byte {","byte};

For example

PROCEDURE -Sigblock* (mask: SET): SET; 82H, 0, 20H, 109, 91H, 0D0H, 20H, 0;

<< BK: A more readable alternative proposed by Steve Metzeler follows, personally I much prefer it. A decision is needed. It would add two new keywords. >>

Interrupts are implemented by marking the procedure with the keyword INTERRUPT as a prefix

INTERRUPT PROCEDURE Proc ...; ... END Proc;

Similarly code procedures are implemented by marking the procedure with the keyword CODE and giving it a body containing hex byte codes or assembler level instructions. CODE PROCEDURE Proc; BEGIN byte {"," byte} | {Assembler Instructions} END Proc;

Interfacing to External Libraries
When Oberon-2 programs are written for external operating systems other than the Oberon System then a mechanism is required to provide interface between them as seamlessly as possible. To avoid performance reduction a direct mapping between Oberon-2 structures and conventions and the external ones is highly desirable. It is also desirable that the notation used should be practical both for large libraries and for individual procedures within a module. It is recognised that use of an external interfacing mechanism renders the module unsafe.

It is recommended that for the benefit of students and newcomers to the language, the documentation of the mechanism bear a health warning in a standard form such as (** NOT SAFE**)

The four elements that must be accommodated for interfacing to external libraries are The following proposal has not been fully tried out however it is offered as a basis for discussion.
 * the Oberon-2 name for the facility
 * the Oberon-2 type and signature of the facility
 * the external name of the facility
 * the location name and calling convention style of the library or object.

For modules containing many procedures all belonging to a single library then the syntax could be MODULE OberonModuleName "[" convention "]" EXTERNAL	"[" externalLibraryName "]" ... Where convention might be "PASCAL" or "C" and externalLibraryName is also a quoted string.

For example: MODULE ISOStrings [ "Modula-2" ] EXTERNAL [ "server_XP/lib" ] ... Normal Oberon-2 identifiers within the module are optionally followed by an equivalent external name as a string, for example: PROCEDURE CreateWindow ["CREATE_WINDOW$BIG"] (... ); Note that the external non-Oberon-2 identifiers or strings may contain any characters which are valid for the external library.

For an Oberon-2 module that contains just one or a few interface procedures, or is hiding the structure of a set of external modules, then the following form can be used. PROCEDURE "["",""]" 	... For example: PROCEDURE ["C","Motif.lib"] CreateWindow ["CREATE_WINDOW"] (... );

<< BK: Please note that Josef Templ and Professor Mössenböck of ETH are strongly against the following two sections being suggested as language extensions. >>

Underscores in Identifiers
Identifiers may contain the additional character "_" ident = (letter | "_") {letter | digit | "_"}. This syntax allows for identifiers to begin with the underscore character "_".

In-line Exponentiation
The exponentiation operator "**" provides a convenient notation for arithmetic expressions, rather than using function calls. It is an arithmetic operator which has a higher precedence than the multiply and divide operators. In the expression a := b**c ; value of the result is the value of b raised to the power of the value of c.

<< BK: This introduces a fifth level of precedence into the language. If we include this at all then the full expression grammar needs to be defined so that it can be implemented consistently. Any volunteers please ... >>

Introduction
There are two main issues regarding control of the compilation process, setting of compilation options for the compiler and selection of the specific source text to be compiled. There are also two main schools of thought about how this control should be specified. Application programmers and project managers often like to have a single source text, especially when a program is designed to have many variants (for example a compiler with very similar code generators for a family of processors). Others prefer to use preprocessors to extract the source text of a particular variant first and then compile it. The trend in the market is to integrate preprocessors into compilers, the main reasons being This appears to be an emotive issue with different organisations having strong loyalty to their own particular approach. In this chapter some conventions are defined for control of compilation with the intention that compiler producers offer such features based on the same basic model. It is recognised that the choice of notation may be prescribed by the operating system in use or to fall in line with the conventions used on existing compilers. Even so where there are opportunities to follow the guidelines and to reduce variation they should be taken.
 * readability of the program for maintainers, being able to see the relations between variants.
 * direct correlation between compiler error messages and the original source text
 * reading the source text only once during the overall compilation process (for speed)
 * saving storage (no intermediate versions)

The additional language constructs defined below should not be considered to be part of the Oberon-2 language. Rather they define a separate compiler control language that coexists with and is distinct from the Oberon-2 language.

All in-line commands to the compiler are contained in ISO style pseudo comments using angled brackets <* ... *>.

Runtime checks
Runtime checks are controlled by pragmas which are used to selectively enable and disable each option. All pragmas should default to provide maximum safety.

Syntax : "<*$" {modifier} "*>"

where modifier is
 * pragma -	set pragma OFF, disable
 * pragma +	set pragma ON, enable
 * <            	stack the current pragma state
 * >            	 unstack the current pragma state
 * !            	revert to the pragma state defined by the original command line.

The following letters are from the ETH OP2 compiler and are only shown as a guide. In practice they are likely to be implementation specific for other compatibility reasons (e.g. other compilers, Unix ...) Source pragmas can be either upper or lower case.

Note: The ETH compilers have a default of - for the R and V pragmas.

Compiler option control
Compiler options can be turned on and off using the statements. As they apply to a whole compilation unit it only makes sense to use them at the beginning of a module.

<*OPTION+ *>      to set OPTION on, enabling it

<*OPTION- *>      to set OPTION off, disabling it

For example <*STANDARD+ *>

The options are Note: There is no option for controlling garbage collection, for example for systems which need deterministic timing. This can be achieved by explicitly calling the system memory manager to turn garbage collection off and on. Also see.

Compiler source control
For large programs where a single source text must support many runtime variants there is a practical need for selective compilation of the source text. The selection can be made either using a preprocessor or, for reasons of optimising disk storage, speed and efficiency, at compiler time.

The syntax for expressing the source text selection is The conditional expression consists of programmer defined SELECTORS which can be combined as an Oberon-like boolean expression which can contain the operators ~, &, OR. Compiler options are in effect predefined selectors and can be used within the condition part

To define a new SELECTOR, which has a default value of FALSE

<* NEW SelectorName *>

To give a SELECTOR a value

<* SelectorName+ *> to set it TRUE

<* SelectorName- *> to set it FALSE

Examples: <*IF ~ MAIN THEN *> ...

<*IF M68000 & WARNINGS THEN *> IMPORT CG68000; <*ELSE *> IMPORT CG80x86; <*END*>

Introduction
This chapter includes recommendations describing some specific characteristics for compilers which confom to these guidelines.

Type ranges
The minimum value that is returned by MAX (type) and the maximum value returned by MIN (type) should be at least (at most) as follows

Type Extension Levels
If an implementation imposes a limit on the number of levels of type extension it should not be less than 8 levels including the base type.

The module SYSTEM
The module SYSTEM should be based on the ETH model wherever reasonable.

The procedure SYSTEM.MOVE
For the procedure SYSTEM.MOVE the behaviour when the source extent and destination extent overlap should be made clear regarding overwriting, also the special case when length = 0.

Garbage collection
Automatic garbage collection is recommended wherever possible. If garbage collection is not available or a mechanism is available to activate and deactivate it then a procedure DISPOSE can be provided in the module system. It takes a single parameter which is a pointer value parameter.

Implementation characteristics
Each compiler implementation inevitably has limits, for example to identifier length or runtime checks provided. A list of characteristics may be provided for each implementation so that users can judge its suitability and any portability problems that might arise when moving between implementations.

The following characteristics are defined

Length of identifier, at least 23 significant characters possible

Record extension levels, 8 including base type

Actual type sizes (INTEGER, LONGINT, ...), see

Initialisation of Pointers
All pointers for procedure variables, variables, record fields and array elements should be initialised by the compiler to a safe value, the value NIL is recommended. This applies to pointers which are statically allocated, dynamically allocated or on stacks. Refer to the ETH change list. (Section ).

The ETH Report does not define that variables are initialised however a practical implementation might include the following approach ...

The compiler should guarantee that level 0 variables of any pointer or procedure type are either statically or dynamically initialised to NIL before the initialisation part of a module (module body) is executed.

The compiler should provide code to dynamically initialise local variables of any pointer or procedure type to NIL before the procedure body is executed.

When executing the predeclared procedure NEW, the storage/heap manager of the run-time system (if any) should initialise the heap space to NIL. Alternatively the compiler should emit code to initialise dynamic variables of any pointer or procedure type to NIL.

A compilation switch may be provided to inhibit the generation of initialisation code for variables of pointer or procedure types. In case of dynamic variables allocated with the procedure NEW, an alternative storage (or run-time system) module may be provided which does no initialisation.

Handling undefined semantics
When operations with undefined semantics, as listed in Section 2.3, occur then their effect is system-dependent and should be handled in consistent ways within a particular implementation. It is expected that the program would terminate with a message indicating the cause and its program location.

Monadic '-'
It should be made clear in documentation supplied with compilers that monadic negation is an addition operator and has a lower precedence then the multiplication operator. For example the expression -5 MOD 3 is equivalent to -(5 MOD 3).

Conversion from Integer to Real
It should be made clear to compiler users that the function LONG cannot be used to convert an expression of LONGINT type to REAL type. There is no explicit function for that purpose. An assignment of the form

real := integer;

has to be used which automatically converts from any integer type to REAL type.

Exported Comments
An exported comment is denoted using two consecutive asterisks after the opening bracket, for example (** this is an exported comment *) It signals to a browser that the comment should be included in a DEFINITION module being derived from the module being processed. It is a convention rather than a language issue.

Read only VAR Parameters
There have been many requests to make ARRAY and RECORD parameters read-only to achieve the efficiency of passing by reference without the associated possibility for corruption of the calling parameter. An attempt to make an assignment to any component of such a read only parameter is a compile-time error. Such parameters could be marked with the standard read only "-" symbol. For example: PROCEDURE Print (theText-: ARRAY OF CHAR) ; Discussions with ETH suggest this is really a compiler code optimisation issue and on this basis it is recommended that this extension is not implemented.

Type Guards with RECORD parameters
If a record is assigned to a formal VAR parameter record, the compiler must generate an implicit type test to make sure that the static type and the dynamic type of the destination record are the same.

Introduction
It is very desirable for programmers that a basic set of library modules is available across a range of different compiler implementations. On the other hand it is also clear that it is impossible to design library modules that are useful for all purposes. To be effective library modules must have a purpose which makes sense for the library user.

This report defines two groups of modules The module definitions provided in are intended to encourage all compiler developers to offer sets of library modules with the same interface and functionality.
 * modules based on the ETH Oberon System designs which provide input-output facilities and support for published teaching material, in particular the series of Oberon books from ETH authors
 * modules which extend the functionality of the language in a standardised way, for example maths libraries.

All implementations should support all the so called basic modules described in Section 6.2. The additional modules should be provided if they are relevant to the particular compiler implementation (e.g. if COMPLEX is supported).

Basic Modules
It is intended that the basic modules are provided with all Oberon-2 compiler implementations. They are based on ETH Oberon System designs ...

Additional Modules
The additional modules are provided with compiler implementations on an 'as needed' basis ...

Basic Library Modules
It is expected that all Oberon-2 compiler implementations will include the following modules ...
 * XYplane
 * Input
 * In
 * Out
 * Files
 * Strings
 * Math and MathL

Additional Modules
The following modules are optional. If they are provided then they should follow the specifications ...
 * Coroutines
 * MathC and MathLC

Module XYplane
Module XYplane provides some basic facilities for graphics programming. Its interface is kept as simple as possible and is therefore more suited for programming exercises than for serious graphics applications.

XYplane provides a Cartesian plane of pixels that can be drawn and erased. The plane is mapped to some location on the screen. The variables X and Y indicate its lower left corner, W its width and H its height. All variables are read-only. DEFINITION XYplane; CONST draw = 1; erase = 0; VAR X, Y, W, H: INTEGER; PROCEDURE Open; PROCEDURE Clear; PROCEDURE Dot (x, y, mode: INTEGER); PROCEDURE IsDot (x, y: INTEGER): BOOLEAN; PROCEDURE Key : CHAR; END XYplane.

Operations

 * Open initializes the drawing plane.
 * Clear erases all pixels in the drawing plane.
 * Dot(x, y, m) draws or erases the pixel at the coordinates (x, y) relative to the lower left corner of the plane. If m=draw the pixel is drawn, if m=erase the pixel is erased.
 * IsDot(x, y) returns TRUE if the pixel at the coordinates (x, y) relative to the lower left corner of the screen is drawn, otherwise it returns FALSE.
 * Key reads the keyboard. If a key was pressed prior to invocation, its character value is returned, otherwise the result is 0X.

Remarks
In the ETH Oberon System Open opens a viewer that takes the whole user track. The contents of this viewer is the drawing plane provided by XYplane.

Origin
Designed by Martin Reiser for the book 'Programming in Oberon'. The above specification was proposed by Hanspeter Mössenböck, ETH

Module Input
Module Input provides facilities to access the mouse, the keyboard, and the clock. DEFINITION Input; VAR TimeUnit: LONGINT; PROCEDURE Available : INTEGER; PROCEDURE Read (VAR ch: CHAR); PROCEDURE Mouse (VAR keys: SET; VAR x, y: INTEGER); PROCEDURE SetMouseLimits (w, h: INTEGER); PROCEDURE Time : LONGINT; END Input.

State

 * Keyboard buffer. A queue of characters typed in from the keyboard.
 * Time. Elapsed time since system startup in units of size 1/TimeUnit seconds.

Operations

 * Available returns the number of characters in the keyboard buffer.
 * Read(ch) returns (and removes) the next character from the keyboad buffer. If the buffer is empty, Read waits until a key is pressed.
 * Mouse(keys, x, y) returns the current mouse position (x, y) in pixels relative to the lower left corner of the screen. keys is the set of the currently pressed mouse keys (left = 2, middle = 1, right = 0).
 * SetMouseLimits(w, h) defines the rectangle where the mouse moves (in pixels). Subsequent calls to the operation Mouse will return coordinates for x in the range 0..w-1 and y in the range 0..h-1.
 * Time returns the time elapsed since system startup in units of size 1/TimeUnit seconds.

Examples
IF Input.Available > 0 THEN Input.Read(ch) END; REPEAT Input.Mouse(keys, x, y); ... draw mouse cursor at position (x, y) ... UNTIL keys = {} seconds := Input.Time DIV Input.TimeUnit

Origin
Part of the ETH Oberon System. The above specification was proposed by H Mössenböck, ETH.

Module In
Module In provides a set of basic routines for formatted input of characters, character sequences, numbers, and names. It assumes a standard input stream with a current position that can be reset to the beginning of the stream. DEFINITION In; VAR Done: BOOLEAN; PROCEDURE Open; PROCEDURE Char (VAR ch: CHAR); PROCEDURE Int (VAR i: INTEGER); PROCEDURE LongInt (VAR i: LONGINT); PROCEDURE Real (VAR x: REAL); PROCEDURE LongReal (VAR y: LONGREAL); PROCEDURE String (VAR str: ARRAY OF CHAR); PROCEDURE Name (VAR name: ARRAY OF CHAR); END In.

State

 * Current position. The character position in the input stream from where the next symbol is read. Open (re)sets it to the beginning of the input stream. After reading a symbol the current position is set to the position immediately after this symbol. Before the first call to Open the current position is undefined.
 * Done. Indicates the success of an input operation. If Done is TRUE after an input operation, the operation was successful and its result is valid. An unsuccessful input operation sets Done to FALSE; it remains FALSE until the next call to Open. In particular, Done is set to FALSE if an attempt is made to read beyond the end of the input stream.

Operations

 * Open (re)sets the current position to the beginning of the input stream. Done indicates if the operation was successful.

The following operations require Done = TRUE and guarantee (Done = TRUE and the result is valid) or (Done = FALSE). All operations except Char skip leading blanks, tabs or end-of-line characters. IntConst = digit {digit} | digit {hexDigit} "H". RealConst = digit {digit} [ {digit} ["E" ("+" | "-") digit {digit}]]. LongRealConst = digit {digit} [ {digit} [("D" |"E") ("+" | "-") digit {digit}]]. StringConst = '"' char {char} '"'. The string must not contain characters less than blank such as EOL or TAB.
 * Char(ch) returns the character ch at the current position.
 * LongInt(n) and Int(n) return the (long) integer constant n at the current position according to the format:
 * Real(n) returns the real constant n at the current position according to the format:
 * LongReal(n) returns the long real constant n at the current position according to the format:
 * String(s) returns the string s at the current position according to the format:
 * Name(s) returns the name s at the current position according to the file name format of the underlying operating system (e.g. "lib/My.Mod" under Unix)

Example: VAR i: INTEGER; ch: CHAR; r: REAL; s, n: ARRAY 32 OF CHAR; ...	 In.Open; In.Int(i); In.Char(ch); In.Real(r); InString(s);In.Name(n) Input stream: 123*1.5  "abc"   Mod.Proc Results: i = 123 ch = "*" r = 1.5E0 s = "abc" n = "Mod.Proc"

Remarks
In the ETH Oberon System the input stream is the text immediately following the most recently invoked command. If this text starts with the character "^" the current position is set to the beginning of the most recent selection (if no selection exists, Done = FALSE). If the text starts with the character "*" the current position is set to the beginning of the text in the marked viewer (if no viewer is marked, Done = FALSE). The end of the input stream is the end of the text containing the current position. There is no provision for input of SHORT integers.

Origin
Designed by Martin Reiser for the book 'Programming in Oberon'. The above specification was proposed by H Mössenböck, ETH.

Module Out
Module Out provides a set of basic routines for formatted output of characters, numbers, and strings. It assumes a standard output stream to which the symbols are written. DEFINITION Out; PROCEDURE Open; PROCEDURE Char (ch: CHAR); PROCEDURE String (str: ARRAY OF CHAR); PROCEDURE Int (i, n: LONGINT); PROCEDURE Real (x: REAL; n: INTEGER); PROCEDURE LongReal (x: LONGREAL; n: INTEGER); PROCEDURE Ln; END Out.

Operations

 * Open initializes the output stream.
 * Char(ch) writes the character ch to the end of the output stream
 * String(s) writes the null-terminated character sequence s to the end of the output stream (without 0X).
 * Int(i, n) writes the integer number i to the end of the output stream. If the textual representation of i requires m characters, i is right adjusted in a field of Max(n, m) characters padded with blanks at the left end. A plus sign is not written.
 * Real(x, n) writes the real number x to the end of the output stream using an exponential form. If the textual representation of x requires m characters (including a two-digit signed exponent), x is right adjusted in a field of Max(n, m) characters padded with blanks at the left end. A plus sign of the mantissa is not written.
 * LongReal(x, n) writes the long real number x to the end of the output stream using an exponential form. If the textual representation of x requires m characters (including a three-digit signed exponent), x is right adjusted in a field of Max(n, m) characters padded with blanks at the left end. A plus sign of the mantissa is not written.
 * Ln writes an end-of-line symbol to the end of the output stream.

Remarks
In the ETH Oberon System the output is appended to an output text that is cleared when module Out is loaded. The output text can be displayed in a new viewer by a call to the procedure Open (Open can also be called as a command).

Origin
Designed by Martin Reiser for the book 'Programming in Oberon'. The above specification was proposed by H Mössenböck, ETH.

Module Files
Module Files provides operations on files and the file directory. DEFINITION Files; IMPORT SYSTEM; TYPE File = POINTER TO Handle; Rider = RECORD eof: BOOLEAN; res: LONGINT; END; PROCEDURE Old (name: ARRAY OF CHAR): File; PROCEDURE New (name: ARRAY OF CHAR): File; PROCEDURE Register (f: File); PROCEDURE Close (f: File); PROCEDURE Purge (f: File); PROCEDURE Delete (name: ARRAY OF CHAR; VAR res: INTEGER); PROCEDURE Rename (old, new: ARRAY OF CHAR; 			 VAR res: INTEGER); PROCEDURE Length (f: File): LONGINT; PROCEDURE GetDate (f: File; VAR t, d: LONGINT); PROCEDURE Set (VAR r: Rider; f: File; pos: LONGINT); PROCEDURE Pos (VAR r: Rider): LONGINT; PROCEDURE Base (VAR r: Rider): File; PROCEDURE Read (VAR r: Rider; VAR x: SYSTEM.BYTE); PROCEDURE ReadInt (VAR R: Rider; VAR x: INTEGER); PROCEDURE ReadLInt (VAR R: Rider; VAR x: LONGINT); PROCEDURE ReadReal (VAR R: Rider; VAR x: REAL); PROCEDURE ReadLReal (VAR R: Rider; VAR x: LONGREAL); PROCEDURE ReadNum (VAR R: Rider; VAR x: LONGINT); PROCEDURE ReadString (VAR R: Rider; VAR x: ARRAY OF CHAR); PROCEDURE ReadSet (VAR R: Rider; VAR x: SET); PROCEDURE ReadBool (VAR R: Rider; VAR x: BOOLEAN;  PROCEDURE ReadBytes (VAR r: Rider; VAR x: ARRAY OF SYSTEM.BYTE; n: LONGINT);	  PROCEDURE Write (VAR r: Rider; x: SYSTEM.BYTE);    PROCEDURE WriteInt (VAR R: Rider; x: INTEGER);   PROCEDURE WriteLInt (VAR R: Rider; x: LONGINT);   PROCEDURE WriteReal (VAR R: Rider; x: REAL);   PROCEDURE WriteLReal (VAR R: Rider; x: LONGREAL);   PROCEDURE WriteNum (VAR R: Rider; x: LONGINT);   PROCEDURE WriteString (VAR R: Rider; x: ARRAY OF CHAR);   PROCEDURE WriteSet (VAR R: Rider; x: SET);   PROCEDURE WriteBool (VAR R: Rider; x: BOOLEAN);   PROCEDURE WriteBytes (VAR r: Rider; VAR x: ARRAY OF SYSTEM.BYTE; n: LONGINT)	END Files.

Types

 * A File represents a stream of bytes usually stored on an external medium. It has a certain length as well as the date and time of its last modification.
 * A file directory is a mapping from file names to files. A file that is not registered in the directory is considered temporary.
 * A Rider is a read/write position in a file (positions start with 0). There may be multiple riders set to the same file. The field eof is set to TRUE if an attempt was made to read beyond the end of the file. The field res reports the success of ReadBytes and WriteBytes operations. Writing data overwrites old data at the rider position. When data is written beyond the end of the file, the file length increases.

Operations on files and the file directory

 * Old(fn) searches the name fn in the directory and returns the corresponding file. If the name is not found, it returns NIL.
 * New(fn) creates and returns a new file. The name fn is remembered for the later use of the operation Register. The file is only entered into the directory when Register is called.
 * Register(f) enters the file f into the directory together with the name provided in the operation New that created f. The file buffers are written back. Any existing mapping of this name to another file is overwritten.
 * Close(f) writes back the file buffers of f. The file is still accessible by its handle f and the riders positioned on it. If a file is not modified it is not necessary to close it.
 * Purge(f) resets the length of file f to 0.
 * Delete(fn, res) removes the directory entry for the file fn without deleting the file. If res=0 the file has been successfully deleted. If there are variables referring to the file while Delete is called, they can still be used.
 * Rename(oldfn, newfn, res) renames the directory entry oldfn to newfn. If res=0 the file has been successfully renamed. If there are variables referring to the file while Rename is called, they can still be used.
 * Length(f) returns the number of bytes in file f.
 * GetDate(f, t, d) returns the time t and date d of the last modification of file f. The encoding is: hour = t DIV 4096; minute = t DIV 64 MOD 64; second = t MOD 64; year = d DIV 512; month = d DIV 32 MOD 16; day = d MOD 32.

Operations on riders

 * Set(r, f, pos) sets the rider r to position pos in file f. The field r.eof is set to FALSE. The operation requires that 0 <= pos < Length(f).
 * Pos(r) returns the position of the rider r.
 * Base(r) returns the file to which the rider r has been set.

Operations for unformatted input and output
In general, all operations must use the following format for external representation:
 * 'Little endian' representation (i.e., the least significant byte of a word is the one with the lowest address on the file).
 * Numbers: SHORTINT 1 byte, INTEGER 2 bytes, LONGINT 4 bytes
 * Sets: 4 bytes, element 0 is the least significant bit
 * Booleans: single byte with FALSE = 0, TRUE = 1
 * Reals: IEEE standard; REAL 4 bytes, LONGREAL 8 bytes
 * Strings: with terminating 0X

Reading

 * Read(r, x) reads the next byte x from rider r and advances r accordingly.
 * ReadInt(r, i) and ReadLInt(r, i) read a (long) integer number i from rider r and advance r accordingly.
 * ReadReal(r, x) and ReadLReal(r, x) read a (long) real number x from rider r and advance r accordingly.
 * ReadNum(r, i) reads an integer number i from rider r and advances r accordingly. The number i is compactly encoded (see remarks below).
 * ReadString(r, s) reads a sequence of characters (including the terminating 0X) from rider r and returns it in s. The rider is advanced accordingly. The actual parameter corresponding to s must be long enough to hold the character sequence plus the terminating 0X.
 * ReadSet(r, s) reads a set s from rider r and advances r accordingly.
 * ReadBool(r, b) reads a Boolean value b from rider r and advances r accordingly.
 * ReadBytes(r, buf, n) reads n bytes into buffer buf starting at the rider position r. The rider is advanced accordingly. If less than n bytes could be read, r.res contains the number of requested but unread bytes.

Writing

 * Write(r, x) writes the byte x to rider r and advances r accordingly.
 * WriteInt(r, i) and WriteLInt(r, i) write the (long) integer number i to rider r and advance r accordingly.
 * WriteReal(r, x) and WriteLReal(r, x) write the (long) real number x to rider r and advance r accordingly.
 * WriteString(r, s) writes the sequence of characters s (including the terminating 0X) to rider r and advances r accordingly.
 * WriteNum(r, i) writes the integer number i to rider r and advances r accordingly. The number i is compactly encoded (see remarks below).
 * WriteSet(r, s) writes the set s to rider r and advances r accordingly.
 * WriteBool(r, b) writes the Boolean value b to rider r and advances r accordingly.
 * WriteBytes(r, buf, n) writes the first n bytes from buf to rider r and advances r accordingly. r.res contains the number of bytes that could not be written (e.g., due to a disk full error).

Examples
VAR f: Files.File; r: Files.Rider; ch: CHAR; Reading from an existing file xxx: f := Files.Old("xxx"); IF f # NIL THEN Files.Set(r, f, 0); Files.Read(r, ch); WHILE ~ r.eof DO ... Files.Read(r, ch) END END Writing to a new file yyy: f := Files.New("yyy"); Files.Set(r, f, 0); Files.WriteInt(r, 8); Files.WriteString(r, " bytes"); Files.Register(f)

Remarks
WriteNum and ReadNum, should use the following encoding algorithms for conversion to and from external format. PROCEDURE WriteNum (VAR r: Rider; x: LONGINT); BEGIN WHILE (x < - 64) OR (x > 63) DO 	   Write(r, CHR(x MOD 128 + 128)); x := x DIV 128 END; Write(r, CHR(x MOD 128)) END WriteNum; PROCEDURE ReadNum (VAR r: Rider; VAR x: LONGINT); VAR s: SHORTINT; ch: CHAR; n: LONGINT; BEGIN s := 0; n := 0; Read(r, ch); WHILE ORD(ch) >= 128 DO     INC(n, ASH(ORD(ch) - 128, s) ); INC(s, 7); Read(r, ch) END; x := n + ASH(ORD(ch) MOD 64 - ORD(ch) DIV 64 * 64, s) END ReadNum; The reason for the specification of the file name in the operation New is to allow allocation of the file on the correct medium from the beginning (if the operating system supports multiple media).

The operations Read, Write, ReadBytes and WriteBytes require the existence of a type SYSTEM.BYTE with the following characteristics:

If a formal parameter is of type SYSTEM.BYTE the corresponding actual parameter may be of type CHAR, SHORTINT, or SYSTEM.BYTE.

If a formal variable parameter is of type ARRAY OF SYSTEM.BYTE the corresponding actual parameter may be of any type. Note that this feature is dangerous and inherently unportable. Its use should therefore be restricted to system-level modules.

Origin
This module is part of the ETH Oberon System. The above specification was proposed by H Mössenböck, ETH.

Module Strings
Module Strings provides a set of operations on strings (i.e., on string constants and character arrays, both of which contain the character 0X as a terminator). All positions in strings start at 0. DEFINITION Strings; PROCEDURE Length (s: ARRAY OF CHAR): INTEGER; PROCEDURE Insert (source: ARRAY OF CHAR;			   pos: INTEGER; 			   VAR dest: ARRAY OF CHAR); PROCEDURE Append (extra: ARRAY OF CHAR;			   VAR dest: ARRAY OF CHAR); PROCEDURE Delete (VAR s: ARRAY OF CHAR;			   pos, n: INTEGER); PROCEDURE Replace (source: ARRAY OF CHAR;			  pos: INTEGER;			   VAR dest: ARRAY OF CHAR); PROCEDURE Extract (source: ARRAY OF CHAR; 			  pos, n: INTEGER; 			   VAR dest: ARRAY OF CHAR); PROCEDURE Pos    (pattern, s: ARRAY OF CHAR; 			   pos: INTEGER): INTEGER; PROCEDURE Cap    (VAR s: ARRAY OF CHAR); END Strings

Operations

 * Length(s) returns the number of characters in s up to and excluding the first 0X.
 * Insert(src, pos, dst) inserts the string src into the string dst at position pos (0 <=pos<=Length(dst)). If pos = Length(dst),src is appended to dst. If the size of dst is not large enough to hold the result of the operation, the result is truncated so that dst is always terminated with a 0X.
 * Append(s,dst) has the same effect as Insert(s,Length(dst),dst).
 * Delete(s, pos, n) deletes n characters from s starting at position pos (0 <= pos œ Length(s)). If n > Length(s) - pos, the new length of s is pos.
 * Replace(src, pos, dst) has the same effect as Delete(dst, pos, Length(src)) followed by an Insert(src, pos, dst).
 * Extract(src, pos, n, dst) extracts a substring dst with n characters from position pos (0 <= pos œ Length(src)) in src. If n > Length(src) - pos, dst is only the part of src from pos to the end of src, i.e. Length(src) -1. If the size of dst is not large enough to hold the result of the operation, the result is truncated so that dst is always terminated with a 0X.
 * Pos(pat, s, pos) returns the position of the first occurrence of pat in s. Searching starts at position pos. If pat is not found, -1 is returned.
 * Cap(s) replaces each lower case letter within s by its upper case equivalent.

Remarks

 * String assignments and string comparisons are already supported by the language Oberon-2.

Origin
This module is loosely based on the ISO Modula-2 Strings library but is much simplified. It was edited by Brian Kirk, Nick Walsh, Josef Templ and Hanspeter Mössenböck.

Module Math and MathL
The module Math provides a basic set of general purpose functions using REAL arithmetic. The module MathL provides the same functions for LONGREAL arithmetic. DEFINITION Math; CONST pi = 3.14159265358979323846; e = 2.71828182845904523536; PROCEDURE  sqrt (x : REAL) : REAL; PROCEDURE power (x,base : REAL) : REAL; PROCEDURE   exp (x : REAL): REAL; PROCEDURE    ln (x : REAL) : REAL; PROCEDURE   log (x,base : REAL) : REAL; PROCEDURE round (x : REAL) : REAL; PROCEDURE   sin (x : REAL) : REAL; PROCEDURE   cos (x : REAL) : REAL PROCEDURE   tan (x : REAL) : REAL; PROCEDURE arcsin (x : REAL) : REAL; PROCEDURE arccos (x : REAL) : REAL; PROCEDURE arctan (x : REAL) : REAL; PROCEDURE arctan2(x,y : REAL): REAL PROCEDURE sinh  (x:REAL):REAL; PROCEDURE cosh  (x:REAL):REAL; PROCEDURE tanh  (x:REAL):REAL; PROCEDURE arcsinh(x:REAL):REAL; PROCEDURE arccosh(x:REAL):REAL; PROCEDURE arctanh(x:REAL):REAL; END Math.

Operations
Note that |x| must not be so close to 1 that the result is less accurate than half precision.
 * sqrt (x) returns the square root of x, where x must be positive
 * sin, cos, tan (x) returns the sine, cosine or tangent value of x, where x is in radians
 * arcsin, arcos, arctan (x) returns the arcsine, arcos, arctan value in radians of x, where x is in the sine, cosine or tangent value
 * power(x, base)	returns the x to the power base
 * round(x) if fraction part of x is in range 0.0 to 0.5 then the result is the largest integer not greater than x, otherwise the result is x rounded up to the next highest whole number. Note that integer values cannot always be exactly represented in REAL or LONGREAL format.
 * ln(x) returns the natural logarithm (base e) of x
 * exp(x)is the exponential of x base e. x must not be so small that this exponential underflows nor so large that it overflows.
 * log(x,base)	is the logarithm of x base b. All positive arguments are allowed. The base b must be positive.
 * arctan2(xn,xd)	is the quadrant-correct arc tangent atan(xn/xd). If the denominator xd is zero, then the numerator xn must not be zero. All arguments are legal except xn = xd = 0.
 * sinh(x)   	is the hyperbolic sine of x. The argument x must not be so large that exp(|x|) overflows.
 * cosh(x)   	is the hyperbolic cosine of x. The argument x must not be so large that exp(|x|) overflows.
 * tanh(x)  	is the hyperbolic tangent of x. All arguments are legal.
 * arcsinh(x) 	is the arc hyperbolic sine of x. All arguments are legal.
 * arccosh(x) 	is the arc hyperbolic cosine of x. All arguments greater than or equal to 1 are legal.
 * arctanh(x) 	is the arc hyperbolic tangent of x. |x| < 1 - sqrt(em), where em is machine epsilon.

Source:
Based on the original ETH Math module, with additions from BK and Al Freed, NASA.

<< BK should the result of round be LONGINT or LONGREAL ? >>

<< AF round (LONGREAL) will have a precision problem anyway. >>

Module Coroutines
Module Coroutines provides non-preemptive threads each with its own stack but otherwise sharing a common address space. Coroutines can explicitly transfer control to other coroutines which are then resumed from the point where they did their last transfer of control. DEFINITION Coroutines; TYPE Coroutine = RECORD END; Body = PROCEDURE; PROCEDURE Init (body: Body; stackSize: LONGINT; 		     VAR cor: Coroutine); PROCEDURE Transfer (VAR from, to: Coroutine); END Coroutines.

Operations

 * Init(p, s, c)	creates and initialises a new coroutine c with a stack of s bytes and a body provided as the procedure p. An initialised coroutine can be started by a Transfer to it. In this case its execution will start at the first instruction of p. Procedure p must never return.
 * Transfer(f, t) transfers control from the currently executing coroutine to the coroutine t. The state of the currently executing coroutine is saved in f. When control is transferred back to f later, f will be restarted in the saved state.

Source:
Proposed by Prof Hanspeter Mössenböck, ETH.

Modules MathC and MathLC
The module MathC provides functions for COMPLEX arithmetic. The module MathLC provides the same functions for LONGCOMPLEX. DEFINITION MathC; PROCEDURE abs (z:COMPLEX):REAL; PROCEDURE power(z:COMPLEX;base:REAL):COMPLEX; PROCEDURE conj(z:COMPLEX):COMPLEX; PROCEDURE sqrt(z:COMPLEX):COMPLEX; PROCEDURE exp (z:COMPLEX):COMPLEX; PROCEDURE ln (z:COMPLEX):COMPLEX; PROCEDURE log (z:COMPLEX, b:REAL):COMPLEX; PROCEDURE sin (z:COMPLEX):COMPLEX; PROCEDURE cos (z:COMPLEX):COMPLEX; PROCEDURE tan (z:COMPLEX):COMPLEX; PROCEDURE arcsin (z:COMPLEX):COMPLEX; PROCEDURE arccos (z:COMPLEX):COMPLEX; PROCEDURE arctan (z:COMPLEX):COMPLEX; PROCEDURE arctan2 (zn,zd:COMPLEX):COMPLEX; PROCEDURE sinh (z:COMPLEX):COMPLEX; PROCEDURE cosh (z:COMPLEX):COMPLEX; PROCEDURE tanh (z:COMPLEX):COMPLEX; PROCEDURE arcsinh (z:COMPLEX):COMPLEX; PROCEDURE arccosh (z:COMPLEX):COMPLEX; PROCEDURE arctanh (z:COMPLEX):COMPLEX; END MathC.

Operations
|Re(z)| = |x| <= 1/sqrt(em) = x(warn) |Re(z)| = |x| <= 1/em = x(max) |Im(z)| = |y| <= ln(bn) = y(max) If |x| is larger than x(warn), then the result will have less than half precision. If |x| is larger than x(max), then the result has no precision. Finally, if |y| is too large, the result will overflow.
 * abs(z) is the absolute value or magnitude of the complex number z. The arguments x and y must not be so large that x*x + y*y overflows. The returned value is a real number.
 * power (Z,base) returns Z to the power base, see comments for exp.
 * conj(z) is the complex conjugate of z. All arguments are legal.
 * sqrt(z) is the complex square root of z. The absolute value of z must not overflow.
 * exp(z) is the complex exponential of z to the base e. The real part of z, i.e. x, should not be so small that the result underflows nor so large that it overflows. If |y| is too large, the result may be less accurate than half precision. If |y| is extremely large, the result will have no precision.
 * ln(z) is the complex natural logarithm (base e) of z. The argument must not be zero, and the absolute value of z must not overflow.
 * log(z,b) is the complex natural logarithm of z to the base b. The argument must not be zero, and the absolute value of z must not overflow. Base b must be positive.
 * sin(z) is the complex sine of z.
 * cos(z) is the complex cosine of z.


 * Re(z)| = |x| <= 1/sqrt(em) = x(warn)


 * Re(z)| = |x| <= 1/em = x(max)


 * Im(z)| = |y| <= ln(bn) = y(max)

If |x| is larger than x(warn), then the result will have less than half precision. If |x| is larger than x(max), then the result has no precision. Finally, if |y| is too large, the result will overflow. |Im(z)| = |y| <= 1/sqrt(em) = y(warn) |Im(z)| = |y| <= 1/em = y(max) |Re(z)| = |x| <= ln(bn) = x(max) If |y| is larger than y(warn), then the result will be less accurate than half precision. If |y| is larger than y(max), the result has no precision.
 * tan(z) is the complex tangent of z. If |cos(z)|**2 is very small, that is, if x is very close to pi/2 or 3*pi/2 and if y is small, then tan(z) is nearly singular. If |cos(z)|**2 is somewhat larger but still small, then the result will be less accurate than half precision. When 2x is so large that sin(2x) cannot be evaluated to any nonzero precision, a special situation results. If |y| < 3/2, then tan cannot be evaluated accurately to better than one significant figure. If 3/2 <= |y|< -0.5*ln(es/2), then tan can be evaluated by ignoring the real part of the argument; however, the answer will be less accurate than half precision.
 * arcsin(z) is the complex arc sine of z. |x| must be less than or equal 1.
 * arccos(z) is the complex arc cosine of z. |x| must be less than or equal to 1.
 * arctan(z) is the complex arc tangent of z. The argument z must not be exactly +/-i, because atan(+/-i) is undefined. In addition, z must not be so close to +/-i that substantial significance is lost.
 * arctan2(zn,zd) is the quadrant-correct, complex, arc tangent atan(zn/zd). The ratio z = zn/zd must not be +/-i, because atan(+/-i) is undefined. Likewise, zn and zd must not be both zero. Finally, z must not be so close to +/-i that substantial significance is lost.
 * sinh(z) is the hyperbolic sine of z.

Finally, if |x| is too large, the result overflows.
 * cosh(z)   	is the hyperbolic cosine of z.


 * Im(z)| = |y| <= 1/sqrt(em) = y(warn)


 * Im(z)| = |y| <= 1/em = y(max)


 * Re(z)| = |x| <= ln(bn) = x(max)

If |y| is larger than y(warn), then the result will be less accurate than half precision. If |y| is larger than y(max), the result has no precision. Finally, if |x| is too large, the result overflows.
 * tanh(z) is the hyperbolic tangent of z. If |cosh(z)|**2 is very small, that is, if y mod 2*pi is very close to pi/2 or 3*pi/2 and if x is small, than tanh(z) is nearly singular. If |cosh(z)|**2 is somewhat larger but still small, then the result will be less accurate than half precision. When 2y is so large that sin(2y) cannot be evaluated accurately to even zero precision, a special situation results. If |x| < 3/2, then tanh cannot be evaluated accurately to better than one significant figure. If 3/2 <=|y| < - 0.5*ln(es/2), then tanh can be evaluated by ignoring the imaginary part of the argument; however, the answer will be less accurate than half precision.
 * arcsinh(z) is the arc hyperbolic sine of z. Almost all arguments are legal. Only when |z| > bn/2 can an overflow occur.
 * arccosh(z) is the arc hyperbolic cosine of z. Almost all arguments are legal. Only when |z| > bn/2 can an overflow occur.
 * arctanh(z) 	is the arc hyperbolic tangent of z. The argument must not be exactly +/-1, because the arc hyperbolic tangent of z is undefined there. In addition, z must not be so close to +/-1 that substantial significance is lost.

Source:
Proposed by Al Freed, NASA. Based on the IMSL package.

Appendix 3 - Oakwood Conference - Croydon 21 .. 23 June 1993 - List of Contributors and Participants
Pre-conference contributions and/or apologies received from the following who were unable to attend ...