Super Prev Next

Translation Rules

Super Prev Next


All comments from the original C text are copied to generated definition modules. Their placement, however, is not preserved in some cases. The COMMENTPOS option may be used to align comments which are placed next to declarations.

C++ compilers are usually able to recognize C++-style comments (beginning with '//') even while operating in C mode. The CPPCOMMENTS option controls whether H2D recognizes such comments as well.

Super Prev Next


In most cases, H2D preserves original C identifiers. Exceptions are structure, union, and enumeration tags, which constitute a separate name space in C. If there is a constant, type, variable, or function identifier which coincides with a tag, H2D appends "_struct", "_union" or "_enum" to that tag.

In some situations, H2D itself generates additional identifiers, e.g. for unnamed function arguments, derived types, and formal types.

H2D may append digits to generated identifiers to avoid conflicts with existent ones.

Identifiers matching Modula-2 keywords are not allowed in source files. However, Modula-2 pervasive identifiers (e.g. INTEGER or HALT) are permitted.

Super Prev Next


The following C declarations were taken from the sys/stat.h file:

struct stat { ... };
int stat( const char *, struct stat * );

TYPE stat_struct = RECORD ... END;
     PtrChar = POINTER TO CHAR;
PROCEDURE stat(arg0: PtrChar; arg1: stat_struct):;

Super Prev Next


C types are translated to Modula-2 types according to the following table:

C type Modula-2 type
base (int, char, etc.) (see Base types mapping)
pointer pointer
array array
enumeration (see Enumeration)
structure record
union variant record
pointer to function procedure type


Super Prev Next


  int    field1;            field1:;
  char   field2;            field2: CHAR;
  double field3;            field3: LONGREAL;
};                        END;

union  UNION {          TYPE UNION = RECORD
  int    field1;            CASE : INTEGER OF
  char   field2;             0: field1:;
  double field3;            |1: field2: CHAR;
};                          |2: field3: LONGREAL;

Super Prev Next

Derived types

For objects declared as having derived types (pointer or array) either a new Modula-2 type is introduced or a previously declared type synonym is used to improve readability. For pointers to base types, a new type is always declared.

In particular, a C structure may contain fields which type is defined as pointer to that structure. In this case H2D also automatically inserts a necessary forward pointer type declaration.

This may cause type compatibility problems. Fortunately, in XDS, compatibility rules for foreign objects are relaxed, e.g. two "C" pointer types are compatible if their base types are the same. Additional setup or postprocessing may be required when H2D is used with third-party Modula-2 compilers.

char *str1;             TYPE
char *str1;               H2D_PtrSChar = POINTER TO CHAR;

                          str1: H2D_PtrSChar;
                          str2: H2D_PtrSChar;

struct s {              TYPE
  int i;                  s = RECORD
};                          i:;
struct s *p;              H2D_Ptrs = POINTER TO s;

                          p: H2D_Ptrs;

struct s {              TYPE
  int i;                  s = RECORD
};                          i:;

typedef struct s *ps;     ps = POINTER TO s;

struct s *p;            VAR
                          p: ps;

struct Node {           TYPE
   int i;                 PtrNode = POINTER TO Node;
   struct Node *next;     Node = RECORD
};                          i:;
                            next: PtrNode;

Super Prev Next


An enumeration (enum) is not actually a distinct type in C --- it is just a convenient way to declare integer constants (but in C++ enumeration is a distinct type). Moreover, since it is possible in C to explicitly specify enumeration constant value, translation to Modula-2 enumeration type may be incorrect. H2D may translate C enumerations into either Modula-2 enumeration types or Modula-2 constant declarations, depending on the GENENUM option setting. For instance, if GENENUM is set to "Const" or "Mixed", the following C type synonym declaration:

typedef enum{ one=1, two } Number;

will be translated to

    (* H2D: enumerated type: Number *)
      one = 1;
      two = 2;
      Number =;
    (* H2D: End of enumerated type: Number *)

If GENENUM is set to "Enum", the same declaration will be translated unsafely (a warning comment will be added):

      Number = (
        one, (* H2D: integer value was 1 *)

Super Prev Next

Type synonyms

C declarations of type synonyms (typedef) are translated to Modula-2 type declarations. If there are multiple synonyms declared for a type, their equivalence is preserved:

typedef char String[256];  TYPE String = ARRAY [0..255] OF CHAR;
typedef String *PString;        PString = POINTER TO String;
typedef String *Buffer;         Buffer = PString;

Note: In C, function type synonyms may be used in function declarations. These synonyms are processed in a way they are processed by a C compiler and do not appear in output files (see Function prototypes).

Super Prev Next


Variables are translated to variables. Variables declared with the const qualifier are translated to read-only variables (XDS extension). The volatile qualifier is currently ignored.

extern int i;              VAR i  :;
extern const int j;        VAR j- :;

Super Prev Next

Function prototypes

C function prototypes declared as void are translated to proper procedure declarations; other are translated to function procedure declarations. If there is no name specified for a function parameter, argx is substituted, where x is a number unique for each unnamed parameter.

In C, a derived type (more precisely, pointer or array) may be specified for a function parameter. In Modula-2, the formal type of a procedure parameter have to be either type name or open array type. H2D translates parameters of array type to open array value parameters.

The translation procedure for pointers is more complicated. By default, H2D searches for a type synonym, previously declared via typedef. The synonym, if found, is used as formal type; otherwise H2D automatically declares one. If automatic declaration is undesirable, required synonyms may be declared in the project file. Other variants of translation may be explicitly specified by means of the #variant directive.

See also Non-standard qualifiers.

Super Prev Next


void p(int,int);              PROCEDURE p ( arg0 :;
                                            arg1 : );

int f(char c);                PROCEDURE f ( c : CHAR ):;

void P(T *t);                 TYPE PtrT = POINTER TO T;
                              PROCEDURE P ( t : PtrT );

void Q(T t[])                 PROCEDURE Q ( t : ARRAY OF T );

int strlen(char *);           PROCEDURE strlen ( arg0 : ARRAY OF CHAR )
#variant strlen(0) : ARRAY                     :;

Super Prev Next

Non-standard qualifiers

In practice, header files are not "pure" ANSI C. The most common extension is a set of additional keywords (qualifiers) which may be used to specify calling/naming conventions used in a particular library or API.

Since XDS provides the similar mechanism called direct language specification (DLS), H2D recognizes a number of such keywords, which are translated to the following DLS strings:

C keyword DLS String
cdecl none
fortran none
interrupt none
pascal "Pascal" for types and variables
"StdCall" for functions
syscall "Syscall"

near, far, and huge qualifiers are recognized but ignored.

Super Prev Next

Preprocessor directives

Super Prev Next

Macro definitions

A #define C preprocessor directive may contain an object-like definition or a function-like definition which are translated differently.

#define identifier Text

If Text is a constant expression, the directive is translated to a constant declaration or a read-only variable declararion (see Preserving constant names). If Text is a type identifier, the directive is translated to a type declaration. If Text is an identifier of a function, a macro definition, or a constant, the directive is translated to a constant declaration. In all other cases, it is interpreted in a C preprocessor manner.

#define identifier"(" idenifier, { identifier } ")" Text

Translated to a proper procedure declaration with all parameters having type ARRAY OF SYSTEM.BYTE. These declarations are marked with a special comment and may be corrected after translation to reflect the actual semantics of a macro by changing parameter types and/or adding return types. See also Native code for information about macro prototype modules.

#undef identifier

Undefines identifier as it is done by a C preprocessor.

Super Prev Next


#define str_constant "Hello World!\n"

#define constant 0x10
#define constant_synonym constant

#define macro_with_params(p1,p2,p3) p1+p2+p3
#define macro_with_params_synonym macro_with_params

int function(int);
#define function_synonym function

typedef int INT;

  str_constant = 'Hello World!' + 12C;
  constant = 10H;
  constant_synonym = constant;

<* IF __GEN_C__ THEN *>

(* H2D: this procedure was generated from Macro. *)
PROCEDURE macro_with_params ( p1, p2, p3: ARRAY OF SYSTEM.BYTE );

<* ELSE *>

PROCEDURE  / macro_with_params ( p1, p2, p3: ARRAY OF SYSTEM.BYTE );

<* END *>

<* IF __GEN_C__ THEN *>

  macro_with_params_synonym = macro_with_params;

<* END *>

PROCEDURE function ( arg0: ):;

  function_synonym = function;

  INT =;


Super Prev Next

File inclusion

#include <file_name>
#include "file_name"

If the file specified by file_name has to be merged with the current file (see Headers merging), H2D treats this directive exactly as a C preprocessor, i.e. replaces it with contents of a specified file. Otherwise, file_name is added to the import list and the file specified by it is translated into a separate definition module. If file_name contains directories, the output files are placed to the same subdirectory.

The GENLONGNAMES option controls conversion of included header file names which contain path:

#include <sys/stat.h>

is translated to

IMPORT stat;


IMPORT sys_stat;


See also Module names.

Super Prev Next

Conditional compilation

H2D handles conditional compilation directives #if, #ifdef, #ifndef, #else, and #endif the same way as a C preprocessor does. A project file may be used to define constants which are used in arguments of these directives.

Super Prev Next

Other directives

H2D recognizes and ignores #line, #error, and #pragma C preprocessor directives.

Super Prev Next

Non-standard preprocessor directives

H2D recognizes two non-standard preprocessor directives: #merge and #variant. These directives are related to definiton module generation only and do not affect the C text, so they may be placed arbitrarily in a header file. Typically they are collected in project files inside a corresponding !header directive.

The advanced technique is to put these directives right into working copies of header files, next to the corresponding declarations. Then, after successful translation of all headers, these directives may be extracted with the help of GENDIRS option and moved to the project file. Now original headers may be used for translation.

Super Prev Next


#merge ( <file_name> | "file_name" )

This directive lists included header files which should be merged even if the MERGEALL option is OFF. This feature may be useful in some cases (see Headers merging).

When placed in a header file, this directive has effect only in this file. When placed in a project file, it has effect in all headers matching the surrounding !header directive.

Super Prev Next


#variant Designator ":" Type
Designator = identifier { "^" | "[ ]" | "." identifier } |
Parameter  = identifier "(" number ")"
Type       = qualident

This form of the #variant directive allows to explicitly specify a Modula-2 Type for an object denoted by Designator. See Base types mapping for more information.

Designator specifies a named object or its element which is subject to the #variant directive:

"^" pointer dereference
"[ ]" array indexing
"." identifier structure or union field selection

Parameter specifies a function identifier and its parameter number (zero-based).

#variant Parameter  ":" ( "VAR" | "ARRAY" | "VAR ARRAY" )

This form is used to control translation of a function Parameter, which has a pointer type.

By default, pointer type function parameters are translated to pointer type procedure parameters (see Function prototypes). The #variant directive allows to specify one of the following alternative rules for a particular parameter:

Modifier T *p is translated to

See also Pointer type function parameters.

#variant f(0) : VAR ARRAY
void f(char*);


A #variant directive has effect only in the file where it is located, or, if specified in a project file, in all files matching the surrounding !header directive. Therefore, Designator should specify an object declared in this file or in one of the files which it includes. If an object specified by Designator is not present, an error message is displayed.

Super Prev Next

Module names

By default, H2D uses a header file name without ".h" extension as a definition module name. If a file name contains characters which are not allowed in identifiers, the !name directive must be used in a project file to specify a proper identifier.