Super Prev Next

Multilanguage programming

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

Super Prev Next

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.

Super Prev Next

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 Modula-2
      M2BASE16+ M2BASE16-
integer 8 SHORTINT
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



    END M.

its portable usage in Oberon-2 is as follows:

       .  .  .

Super Prev Next

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:

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:

      Rec = RECORD END;
      MP  = POINTER ["Modula"] TO Rec; (* Modula pointer *)
      OP  = POINTER TO Rec;     (* Oberon pointer *)
      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.

Super Prev Next

Example: Using the Modula data type in Oberon

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

      r: Rec;
      p: Ptr;

    PROCEDURE Foo(VAR r: Rec);

    END m2.

    (* Oberon-2 *) MODULE o2;

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

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

      NEW(p);     (* Modula-2 default ALLOCATE *)
      NEW(x);     (* Oberon-2 NEW *)
    END o2.

Super Prev Next

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.

Super Prev Next

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 expression /We 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


      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 :;
     .  .  .
      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.

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

    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.

Super Prev Next

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.

Super Prev Next

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.

Super Prev Next


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


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

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

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

Super Prev Next

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:;

Super Prev Next

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".

Super Prev Next

Assignment compatibility

Two pointer type objects are considered assignment compatible, if

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

Super Prev Next

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:

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

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.

Super Prev Next


      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;

      s: Str;
      a: ARRAY [0..5] OF CHAR;
      R: Rec;
      A: ARRAY [0..20] OF 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 *)

Super Prev Next

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: