# XDS Oberon-2

This chapter includes the details of the Oberon-2 language which are specific for this implementation. In the standard modeWhen the options O2EXTENSIONS and O2NUMEXT are OFF. XDS Oberon-2 is fully compatible with ETH compilers (See The Oberon-2 Report). The last changes to the language are described in Last changes to the language.

To provide a smooth path from Modula-2 to Oberon-2 XDS allows all Modula-2 data types to be used in Oberon-2 modules (See Using Modula-2 features).

Several language extensions are implemented in the language according to The Oakwood Guidelines for the Oberon-2 Compiler Developers 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. (See Oakwood numeric extensions). Other language extensions are described in Language extensions. As XDS is a truly multi-lingual system, special features were introduced to provide interfacing to foreign languages (See Chapter Multilanguage programming).

- The Oberon environment
- Last changes to the language
- Oakwood numeric extensions
- Using Modula-2 features
- Language extensions
- The Oberon-2 module SYSTEM

## The Oberon environment

The Oberon-2 language was originally designed for use in an environment that provides command activation, garbage collection, and dynamic loading of the modules. Not being a part of the language, these features still contribute to the power of Oberon-2.

The garbage collector and command activation are implemented in the Oberon Run-Time Support and can be used in any program. The dynamic loader is not provided in the current release. See The oberonRTS module for further information.

- Program structure
- Creating a definition

### Program structure

In an Oberon-2 environment, any declared parameterless procedure can be considered as a main procedure and can be called by its name (a qualified identifier of the form `ModuleName.ProcName`).

Due to the nature of XDS, and its freedom from the Oberon system, a different approach had to be found to declare the `top level' or program modules.

The module which contains the top level of your program must be compiled it with the MAIN option set. This will generate an entry point to your program. Only one module per program shall be compiled with the option set. It is recommended to set it in the module header:

        <*+ MAIN *>
        MODULE hello;

        IMPORT InOut;

        BEGIN
          InOut.WriteString ("Hello World!");
          InOut.WriteLn;
        END hello.

### Creating a definition

XDS provides two different ways to create a definition for an Oberon-2 module:

- the BROWSE operation mode creates a definition module from a symbol file
- the MAKEDEF option forces the Oberon-2 compiler to generate a (pseudo) definition module after successful compilation of an Oberon-2 module.

The MAKEDEF option provides additional services: the compiler will preserve the so-called exported comments (i.e. comments which start with `(**') if the XCOMMENTS option is ON.

The generated pseudo-definition module contains all exported declarations in the order of their appearance in the source text. All exported comments are placed at the appropriate positions.

A definition can be generated in three styles.The BSTYLE equation can be used to choose one of the styles: DEF (default), DOC or MOD.

The DEF style

  This produces an ETH-style definition module. All type-bound procedures (methods) and relative comments are shown as parts of the corresponding record types.

  This is the only style for which the BSREDEFINE and BSCLOSURE options are applicable.

The DOC style

  This produces a pseudo-definition module in which methods are shown as parts of the appropriate record types (ignoring comments) and at the positions at which they occur in the source text.

The MOD style

  This attempts to produce a file which can be compiled as an Oberon-2 module after slight modification (i.e. the file will contain "`END procname`", etc.)

## Last changes to the language

- ASSERT
- Underscores in identifiers
- Source code directives

### ASSERT

The procedure `ASSERT` checks its boolean parameter and terminates the program if it is not `TRUE`. The second optional parameter denotes a task termination code. If omitted, a standard value is assumed.

`PROCEDURE ASSERT(cond: BOOLEAN [; code: INTEGER]);`

A call `ASSERT(expr,code)` is equivalent to

`IF NOT expr THEN HALT(code) END;`

### Underscores in identifiers

According to the Oakwood Guidelines an underscore ("`_`") may be used in identifiers (as a letter).

`ident = ( letter | "_" ) { letter | digit | "_" }`

We recommend to use underscores with care, as it may cause problems with software portability to other compilers. This feature may be important for interfacing to foreign languages (See Chapter Multilanguage programming).

### Source code directives

Source code directives (or pragmas) are used to set compilation options in the source text and to select specific pieces of the source text to be compiled (conditional compilation). According to the Oakwood Guidelines all directives are contained in ISO Modula-2 style pseudo comments using angled brackets `<* ... *>`.

The additional language constructs should not be considered to be part of the Oberon-2 language. They define a separate compiler control language that coexist with Oberon-2. The option O2ISOPRAGMA allows pragmas to be used.

The syntax of the directives is the same for Modula-2 and Oberon-2. See Source code directives for further details.

## Oakwood numeric extensions

XDS Oberon-2 supports two extensions which are of importance for scientific programming, namely

- complex numbers
- in-line exponentiation operator

The O2NUMEXT option should be set to use these extensions.

- Complex numbers
- In-line exponentiation

### Complex numbers

NOTE: Only valid when option O2NUMEXT is set.

Two additional types are included in the type hierarchy if the O2NUMEXT option is set:

`COMPLEX` | defined as | `(REAL,REAL)`
`LONGCOMPLEX` | defined as | `(LONGREAL,LONGREAL)`

All numeric types form a (partial) hierarchy whole types REAL

l COMPLEX
LONGREAL
LONGCOMPLEX A common mathematical notation is used for complex number literals:

`number = integer | real | complex`
`complex = real "i"`

A literal of the form `5.0i` denotes a complex number with real part equal to zero and an imaginary part equal to 5.0. Complex constants with a non-zero real part can be described using arithmetic operators.

        CONST
          i = 1.i;
          x = 1. + 1.i;

For the declarations

        VAR
          c: COMPLEX;
          l: LONGCOMPLEX;
          r: REAL;
          x: INTEGER;

the following statements are valid:

          c:=i+r;
          l:=c;
          l:=c*r;
          l:=l*c;

New conversion functions `RE` and `IM` can be used to obtain a real or imaginary part of a value of a complex type. Both functions have one parameter. If the parameter is of the `COMPLEX` type, both functions return a `REAL` value; if the parameter is of the `LONGCOMPLEX` type, functions return a `LONGREAL` value; otherwise the parameter should be a complex constant and functions return a real constant.

A complex value can be formed by applying the standard function `CMPLX` to two reals. If both `CMPLX` arguments are real constants, the result is a complex constant.

`CONST i = CMPLX(0.0,1.0);`

If both expressions are of the `REAL` type, the function returns a `COMPLEX` value, otherwise it returns a `LONGCOMPLEX` value.

### In-line exponentiation

NOTE: Only valid when option O2NUMEXT is set.

The exponentiation operator `**` provides a convenient notation for arithmetic expressions, which does not involve function calls. It is an arithmetic operator which has a higher precedence than multiplication operators.

        Term     =  Exponent { MulOp Exponent }.
        Exponent =  Factor { "**" Factor }.

Note: the operator is right-associated: a**b**c is evaluated as a**(b**c)

The left operand of the exponentiation (`a**b`) should be any numeric value (including complex), while the right operand should be of a real or integer type. The result type does not depend of the type of right operand and is defined by the table:

Left operand type | Result type
an integer type | `REAL`
`REAL` | `REAL`
`LONGREAL` | `LONGREAL`
`COMPLEX` | `COMPLEX`
`LONGCOMPLEX` | `LONGCOMPLEX`

## Using Modula-2 features

All Modula-2 types and corresponding operations can be used in Oberon-2, including enumeration types, range types, records with variant parts, sets, etc.

##### Important Notes:

- It is not allowed to declare Modula-2 types in an Oberon-2 module.
- A module using Modula-2 features is likely to be non-portable to other compilers.

##### Example

        (*MODULA-2*) DEFINITION MODULE UsefulTypes;

        TYPE
          TranslationTable = ARRAY CHAR OF CHAR;
          Color  = (red,green,blue);
          Colors = SET OF Color;

        END UsefulTypes.

        (*OBERON-2*) MODULE UsingM2;

        IMPORT UsefulTypes;

        TYPE
          TranslationTable* = UsefulTypes.TranslationTable;

        VAR colors*: UsefulTypes.Color;

        BEGIN
          colors:=UsefulTypes.Colors{UsefulTypes.red};
        END UsingM2.

## Language extensions

Warning: Using extensions may cause problems with the software portability to other compilers.

In the standard mode, the XDS Oberon-2 compiler is fully compatible with ETH compilers (See also Last changes to the language). The O2EXTENSIONS option enables some language extensions. The main purposes of language extensions are

- to improve interfacing to other languages (See Chapter Multilanguage programming).
- to provide backward compatibility with the previous versions of XDS.

See also

- Source language directives (Source code directives)
- Oakwood numeric extensions (Oakwood numeric extensions).

- Comments
- String concatenation
- VAL function
- Read-only parameters
- Variable number of parameters
- Value constructors

### Comments

NOTE: Only valid when option O2EXTENSIONS is set.

As well as "`(**)`", there is another valid format for comments in source texts. The portion of a line from "`--`" to the end of line is considered a comment.

`VAR j: INTEGER; -- this is a comment`

### String concatenation

NOTE: Only valid when option O2EXTENSIONS is set.

The symbol "+" can be used for constant string and characters concatenation. See Strings for more details.

### VAL function

NOTE: Only valid when option O2EXTENSIONS is set.

The function `VAL` can be used to obtain a value of the specified scalar type from an expression of a scalar type. See Type conversions for more details.

`PROCEDURE VAL(Type; expr: ScalarType): Type;`

The function can be applied to any scalar types, including system fixed size types (See Whole system types).

### Read-only parameters

NOTE: Only valid when option O2EXTENSIONS is set.

In a formal parameter section, the symbol `"-"` may appear after a name of a value parameter. That parameter is called read-only; its value can not be changed in the procedure's body. Read-only parameters need not to be copied before the procedure activation; this enables procedures with structured parameters to be more effective. Read-only parameters can not be used in a procedure type declaration.

We recommend to use read-only parameters with care. The compiler does not check that the read-only parameter is not modified via another parameter or a global variable.

##### Example

        PROCEDURE Foo(VAR dest: ARRAY OF CHAR;
                       source-: ARRAY OF CHAR);
        BEGIN
          dest[0]:='a';
          dest[1]:=source[0];
        END Foo;

The call `Foo(x,x)` would produce a wrong result, because the first statement changes the value of `source[0]` (`source` is not copied and points to the same location as `dest`).

### Variable number of parameters

NOTE: Only valid when option O2EXTENSIONS is set.

Everything contained in the section Variable number of parameters is applicable to Oberon-2.

### Value constructors

NOTE: Only valid when option O2EXTENSIONS is set.

Everything contained in the section Value constructors is applicable to Oberon-2.

## The Oberon-2 module SYSTEM

Low-level facilities are provided by the module `SYSTEM`. This module does not exist in the same sense as other library modules; it is hard-coded into the compiler itself. However, to use the provided facilities, it must be imported in the usual way.

Some procedures in the module `SYSTEM` are generic procedures that cannot be explicitly declared, i.e. they apply to classes of operand types.

XDS Oberon-2 compiler implements all system features described in The Oberon-2 Report (except `GETREG`, `PUTREG`, and `CC`) and allows one to access all features, described in the Modula-2 International Standard Modula-2 (See The Modula-2 module SYSTEM). In this section we describe only features specific for this implementation.

- Compatibility with BYTE
- Whole system types
- NEW and DISPOSE
- M2ADR

### Compatibility with BYTE

Expressions of types `CHAR`, `BOOLEAN`, `SHORTINT` and `SYSTEM.CARD8` can be assigned to variables of type `BYTE` or passed as actual parameters to formal parameters of type `BYTE`.

If a formal procedure parameter has type `ARRAY OF BYTE`, then the corresponding actual parameter may be of any type, except numeric literals.

### Whole system types

Module `SYSTEM` contains the signed types `INT8`, `INT16`, `INT32`, and unsigned types `CARD8`, `CARD16`, `CARD32`, which are guaranteed to contain exactly 8, 16, or 32 bits respectively. These types were introduced to simplify consstructing the interfaces to foreign libraries (See Chapter Multilanguage programming). The basic types `SHORTINT`, `INTEGER`, `LONGINT` are synonyms of `INT8`, `INT16`, and `INT32` respectively.

The unsigned types form a hierarchy whereby larger types include (the values of) smaller types. `SYSTEM.CARD32` `SYSTEM.CARD16` `SYSTEM.CARD8` The whole hierarchy of numeric types (See also Complex numbers): `LONGREAL` `REAL` {

l signed types
unsigned types
.

### NEW and DISPOSE

The procedure `SYSTEM.NEW` can be used to allocate the system memory, i.e. memory which is not the subject of garbage collection. `SYSTEM.NEW` is a generic procedure, which is applied to pointer types and can be used in several ways, depending on pointer's base type.

`PROCEDURE NEW(VAR p: AnyPointer [; x0,..xn: integer]);`

Let type `P` be defined as `POINTER TO` T and p is of type `P`.

`NEW(`p`)` | T is a record or fixed length array type. The procedure allocates a storage block of `SIZE(`T`)` bytes and assigns its address to p.
`NEW(`p`,`n`)` | T is a record or fixed length array type. The procedure allocates a storage block of n bytes and assigns its address to p.
`NEW(`p`,`x0`,..`xn-1`)` | T is an n-dimensional open array. The procedure allocates an open array of lengths given by the expressions x0,..xn-1

The procedure `SYSTEM.DISPOSE` can be used to free a block previously allocated by a call to `SYSTEM.NEW`. It does not immediately deallocate the block, but marks it as a free block. The block will be deallocated by the next call of the garbage collector.

`PROCEDURE DISPOSE(VAR p: AnyPointer; [size: integer]);`

`DISPOSE(`p`)` | T is a record or array type. The procedure deallocates the storage block p points to.
`DISPOSE(`p`,`n`)` | T is a record or fixed length array type. The procedure deallocates the storage block of n bytes p points to.

### M2ADR

In Oberon-2, the `SYSTEM.ADR` procedure returns `LONGINT`, which is not always very convenient. The `SYSTEM.M2ADR` procedure behaves as Modula-2 `SYSTEM.ADR`, returning `SYSTEM.ADDRESS`:

`PROCEDURE M2ADR(VAR x: any type): ADDRESS;`