# Multilanguage programming

XDS allows you to mix Modula-2, Oberon-2, C, and Assembler modules, libraries, and object files in one project.

- Modula-2 and Oberon-2
- Direct language specification
- Interfacing to C
- Relaxation of compatibility rules

## Modula-2 and Oberon-2

It is not necessary to notify the compiler of using Modula-2 objects in Oberon-2 module and vice versa. The compiler will detect the language automatically when processing symbol files on `IMPORT` clause.

- Basic types
- Data structures
- Garbage collection

### Basic types

In Oberon-2 the basic types have the same length on all platforms. In Modula-2 the size of types `INTEGER`, `CARDINAL` and `BITSET` may be different and depends on the value of the M2BASE16 option. The following table summarizes the correspondence between the basic types.

Type | Size | Oberon-2 | 2c|Modula-2
|  |  | `M2BASE16+` | `M2BASE16-`
integer | 8 | `SHORTINT` | --- | ---
integer | 16 | `INTEGER` | `INTEGER` | ---
integer | 32 | `LONGINT` | --- | `INTEGER`
cardinal | 8 | --- | --- | ---
cardinal | 16 | --- | `CARDINAL` | ---
cardinal | 32 | --- | --- | `CARDINAL`
bitset | 16 | --- | `BITSET` | ---
bitset | 32 | `SET` | --- | `BITSET`

The system types `INT` and `CARD` correspond to Modula-2 `INTEGER` and `CARDINAL` types respectively. We recommend to use `INT` and `CARD` in Oberon-2 when importing Modula-2 modules. For example, if the procedure `Foo` is defined in the Modula-2 definition module `M` as

        DEFINITION MODULE M;

        PROCEDURE Foo(VAR x: INTEGER);

        END M.

its portable usage in Oberon-2 is as follows:

        VAR x: SYSTEM.INT;
           .  .  .
          M.Foo(x);

### Data structures

XDS allows any Modula-2 data structures to be used in Oberon-2 modules, even those that can not be defined in Oberon-2 (e.g. variant records, range types, set types, enumerations, etc).

However, usage of Modula-2 types in Oberon-2 and vice versa is restricted. Whenever possible XDS tries to produce the correct code. If a correct translation is impossible, an error is reported:

- a Modula-2 record field type cannot be of an Oberon-2 pointer, record or array type;
- a Modula-2 pointer to an Oberon-2 record cannot be used in specific Oberon-2 constructs (type-bound procedures, type guards, etc);
- an opaque type can not be defined as an Oberon pointer.

Standard procedures `NEW` and `DISPOSE` are always applied according to the language of a parameter's type. For example, for the following declarations in an Oberon-2 module:

        TYPE
          Rec = RECORD END;
          MP  = POINTER ["Modula"] TO Rec; (* Modula pointer *)
          OP  = POINTER TO Rec;     (* Oberon pointer *)
        VAR
          m: MP;
          o: OP;

the call `NEW(m)` will be treated as a call to the Modula-2 default `ALLOCATE`, while `NEW(o)` will be treated as a call of the standard Oberon-2 run-time routine. See also Direct language specification.

Implicit memory deallocation (garbage collection) is applied to Oberon-2 objects only. If a variable of a Modula-2 pointer type is declared in an Oberon-2 module, it shall be deallocated explicitly.

##### Example: Using the Modula data type in Oberon

        (* Modula-2*) DEFINITION MODULE m2;
        TYPE
          Rec = RECORD  (* a record with variant parts *)
            CASE tag: BOOLEAN OF
              |TRUE:  i: INTEGER;
              |FALSE: r: REAL;
            END;
          END;
          Ptr = POINTER TO Rec;

        VAR
          r: Rec;
          p: Ptr;

        PROCEDURE Foo(VAR r: Rec);

        END m2.

        (* Oberon-2 *) MODULE o2;

        IMPORT m2; (* import of a Modula-2 module *)

        VAR
          r: m2.Rec;  (* using the Modula-2 record type *)
          p: m2.Ptr;  (* using the Modula-2 pointer type *)
          x: POINTER TO m2.Rec;

        BEGIN
          NEW(p);     (* Modula-2 default ALLOCATE *)
          NEW(x);     (* Oberon-2 NEW *)
          m2.Foo(r);
          m2.Foo(p^);
          m2.Foo(x^);
        END o2.

### Garbage collection

It is important to remember that Modula-2 and Oberon-2 have different approaches to memory utilization. When a program contains both Modula-2 and Oberon-2 modules, garbage collection is used. See Memory management for more information.

## Direct language specification

The compiler must know the implementation language of a module to take into account different semantics of different languages and to produce correct code.

In some cases, it is necessary for a procedure or data type to be implemented according to the rules of a language other than that of the whole module. In XDS, it is possible to explicitly specify the language of a type or object. Direct language specification (DLS) is allowed either if language extensions are enabled or if the module `SYSTEM` is imported.

In a record, pointer, or procedure type declaration, or in a procedure declaration, the desired language (or, more precisely, the way in which that declaration is treated by the compiler) can be specified as `"[" language "]"` immediately following the keyword `RECORD`, `POINTER`, or `PROCEDURE`. `language` can be a string or integer constant expressionWe recommend to use strings, integer values are preserved for backward compatibility.:

Convention | String | Integer
Oberon-2 | `"Oberon"` | `0`
Modula-2 | `"Modula"` | `1`
C | `"C"` | `2`
Pascal | `"Pascal"` | `5`
Win32 API | `"StdCall"` | `7`
OS/2 API | `"SysCall"` | `8`

Examples:

`TYPE`
`UntracedPtr = POINTER ["Modula"] TO Rec;`

Here `UntracedPtr` is defined as a Modula-2 pointer, hence all variables of that type will not be traced by garbage collector.

`PROCEDURE ["C"] sig_handler (id : SYSTEM.int);`
`. . .`
`signal.signal(signal.SYSSEGV, sig_handler);`

Here `sig_handler` has C calling and naming conventions, so it can be installed as a signal handler into C run-time support.

A direct language specification clause placed after a name of a field, constant, type, or variable points out that the name of the object will be treated according to the rules of the specified language.

        TYPE
          Rec ["C"] = RECORD
            name ["C"]: INTEGER;
          END;

        CONST pi ["C"] = 3.14159;

        VAR buffer[]["C"]: POINTER TO INTEGER;

Note: In ISO Modula-2, an absolute address may be specified for a variable after its name in square brackets, so the empty brackets are required in the last line.

A procedure name is treated according to the language of its declaration, so in the following declaration:

`PROCEDURE ["C"] Foo;`

both the procedure type and the procedure name are treated according to the C language rules. Note: If you are using a C++ compiler, the `Foo` function should be declared with C name mangling style. Consult your C++ manuals for further information.

## Interfacing to C

Special efforts were made in XDS to provide convenient interface to other languages, primarily to the C language. The main goal is to allow direct usage of existing C libraries and APIs in Modula-2/Oberon-2 programs.

- Foreign definition module
- External procedures specification

### Foreign definition module

A direct language specification clause may appear immediately after keywords `DEFINITION` `MODULE`. The effect is that all objects defined in that module are translated according to the specified language rules, thus making unnecessary direct language specifications for each object.

Several options are often used in foreign definition modules. See Foreign language interface for the description of options used to create a foreign definition module.

##### Example

        <*+ M2EXTENSIONS *>
        <*+ CSTDLIB *>      (* C standard library *)
        <*+ NOHEADER *>     (* we already have header file *)
        DEFINITION MODULE ["C"] string;

        IMPORT SYSTEM;

        PROCEDURE strlen(s: ARRAY OF CHAR): SYSTEM.size_t;
        PROCEDURE strcmp(s1: ARRAY OF CHAR;
                         s2: ARRAY OF CHAR): SYSTEM.int;
        END string.

Take the following considerations into account when designing your own foreign definition module:

- If you are developing an interface to an existing header file, use the NOHEADER option to disable generation of the header file. This option is meaningful for translators only.
- If the header file is a standard header file, use the CSTDLIB option. This option is meaningful for the translators only.
- Use the special `SYSTEM` types `int`, `unsigned`, `size_t`, and `void` for corresponding C types.
- XDS compilers use relaxed type compatibility rules for foreign entities. See Relaxation of compatibility rules for more information.

Definition modules for ANSI C libraries (`stdio.def`, `string.def`, etc) can be used as tutorial examples.

### External procedures specification

In some cases, it may be desirable not to write a foreign definition module but to use some C or API functions directly. XDS compilers allow a function to be declared as external.

The declaration of an external procedure consists of a procedure header only. The procedure name in the header is prefixed by the symbol `"/"`.

`PROCEDURE ["C"] / putchar(ch: SYSTEM.int): SYSTEM.int;`

## Relaxation of compatibility rules

The compiler performs all semantic checks for an object or type according to its language specification. Any object declared as that of Modula-2 or Oberon-2 is subject to Modula-2 or Oberon-2 compatibility rules respectively. The compiler uses relaxed compatibility rules for objects and types declared as `"C"`, `"Pascal"`, `"StdCall"`, and `"SysCall"`.

- Assignment compatibility
- Parameter compatibility
- Ignoring function result

### Assignment compatibility

Two pointer type objects are considered assignment compatible, if

- they are of the same Modula-2 or Oberon-2 type.
- at least one of their types is declared as `"C"`, `"Pascal"`, `"StdCall"`, or `"SysCall"`, and their base types are the same.

        VAR
          x: POINTER TO T;
          y: POINTER TO T;
          z: POINTER ["C"] TO T;
        BEGIN
          x := y;       -- error
          y := z;       -- ok
          z := y;       -- ok

### Parameter compatibility

For procedures declared as `"C"`, `"Pascal"`, `"StdCall"`, or `"SysCall"`, the type compatibility rules for parameters are significantly relaxed:

If a formal value parameter is of the type declared as `POINTER TO T`, the actual parameter can be of any of the following types:

- the same type (the only case for regular Modula-2/Oberon-2 procedures);
- another type declared as `POINTER TO T`.
- any array type which elements are of type `T`. In this case the address of the first array element is passed, as it is done in C.
- the type `T` itself, if `T` is a record type. In this case the address of the actual parameter is passed.

If a formal parameter is an open array of type `T`, the actual parameter can be of any of the following types:

- an (open) array of type `T` (the only case for regular Modula-2/Oberon-2 procedures);
- type verb'T' itself (if M2EXTENSIONSoption is set ON);
- any type declared as `POINTER TO T`.

This relaxation, in conjunction with the `SYSTEM.REF` function procedure, simplifies Modula-2/Oberon-2 calls to C libraries and the target operating system API, preserving the advantages of the type checking mechanism provided by that languages.

##### Example

        TYPE
          Str = POINTER TO CHAR;
          Rec = RECORD ... END;
          Ptr = POINTER TO Rec;

        PROCEDURE ["C"] Foo(s: Str); ... END Foo;
        PROCEDURE ["C"] Bar(p: Ptr);  ... END Bar;
        PROCEDURE ["C"] FooBar(a: ARRAY OF CHAR);  ... END FooBar;

        VAR
          s: Str;
          a: ARRAY [0..5] OF CHAR;
          p: POINTER TO ARRAY OF CHAR;
          R: Rec;
          A: ARRAY [0..20] OF REC;
          P: POINTER TO REC;

          Foo(s);    (* allowed - the same type *)
          Foo(a);    (* allowed for the "C" procedure *)
          Foo(p^);   (* allowed for the "C" procedure *)
          Bar(R);    (* the same as Bar(SYSTEM.REF(R)); *)
          Bar(A);    (* allowed for the "C" procedure *)
          Bar(P);    (* allowed for the "C" procedure *)
          FooBar(s); (* allowed for the "C" procedure *)

### Ignoring function result

It is a standard practice in C programming to ignore the result of a function call. Some standard library functions are designed taking that practice into account. E.g. the string copy function accepts the destination string as a variable parameter (in terms of Modula-2) and returns a pointer to it:

`extern char *strcpy(char *, const char *);`

In many cases, the result of the `strcpy` function call is ignored.

In XDS, it is possible to ignore results of functions defined as `"C"`, `"Pascal"`, `"StdCall"`, or `"SysCall"`. Thus, the function `strcpy` defined in the `string.def` foreign definition module as

`PROCEDURE ["C"] strcpy(VAR d: ARRAY OF CHAR;`
`s: ARRAY OF CHAR): ADDRESS;`

can be used as a proper procedure or as function procedure:

`strcpy(d,s);`
`ptr:=strcpy(d,s);`