modula-2 home

  Home  
  Tutorial  
  Win32 API  
  Reference  
  Projects  
 

 

Modules

Modules are the unit of program decomposition.  Modula-2 supports both separately compiled modules and local modules within a compilation unit.

Definition Modules

A definition module is defined by:

DEFINITION MODULE ModuleName;

  {Import}

  {Declaration}

END ModuleName.

Import is:

[FROM ModuleName] IMPORT
   identifier {,identifier};

Each declaration in a definition module is available to other modules that import the definition module.

For compatibility with earlier versions of Modula-2, an export list can follow the imports in a definition module.  Stony Brook Modula-2 ignores the export list for definition modules.

Procedures in definition modules

Procedure declarations in definition modules can specify only the procedure header, which consists of the name of the procedure, the parameters, the value type, and the procedure attributes.  Any local declarations and the code of the procedure are in the implementation module.

Opaque types

Definition modules can contain type declarations of the following form:

TYPE name;

This is called an opaque type declaration because the actual type is not visible to the user of the module.  The type name can be used in other declarations within the definition module.

The corresponding implementation module must contain a full type declaration for any opaque types declared in the definition module.  The full type declaration must define a pointer type.

A module importing an opaque type can declare objects of that type, do assignments of objects of that type, compare two objects of the same opaque type, and pass objects of the type as parameters only.  The importing module cannot allocate, deallocate or dereference the type since it has no knowledge of the type to which it points.

Opaque types are used to hide information from the user of a module. They let the user perform only the minimum operations on a data type, for two reasons:

  • The integrity of the data structure is ensured because operations on the actual data can be performed only by the implementation module itself.
  • The user cannot be aware of the actual implementation of the data type. If the implementor decides to change the data structure, users of the data type are not affected.

Implementation Modules

Implementation modules take the following form:

IMPLEMENTATION MODULE ModName [[Protection]];
 {Import}
 {Declaration}
[ BEGIN
  ListOfStatements
[EXCEPT
  ListOfStatements]
[
 FINALLY
  ListOfStatements
 EXCEPT
  ListOfStatements
]
]
END ModName.

An implementation module with the same module name must exist for each definition module.  The implementation module contains the actual code for the procedures defined in the definition modules.  It also allocates the space for variables declared in the definition module.

Each procedure declared as a header in the definition module must be fully declared in the implementation module.  The types of parameters, type of return value, public name, and procedure attributes must be identical to the declaration in the definition module, since that is where other modules get the information about the procedure.

Variables, types, and constants declared in the definition module must not be redeclared in the implementation module, with the exception of opaque type declarations.  These symbols are all made visible in the implementation module automatically by the compiler.

The implementation module can also contain its own declarations that are not contained in the definition module. These declarations are not visible anywhere else but the implementation module.

Program Modules

A program module contains the main program of a Modula-2 program.  Program modules take the following form:

MODULE ModName [[Protection]];
 {Import}
 {Declaration}
BEGIN
   ListOfStatements
[EXCEPT
   ListOfStatements]
END ModName.

The ListOfStatements is the code for the main program.  Every Modula-2 program must have a program to define where execution starts. Similarly, you cannot have two program modules linked together in a single program.

Import Declarations

The import declaration is a special declaration that makes symbols from another module available.  If you are going to use symbols declared in another module, they must be mentioned in an import declaration.

The import declaration takes either of the following forms:

IMPORT IdList;

or

FROM ModName IMPORT IdList;

Where IdList is:

identifier {,identifier}

In the first form of the import declaration, IdList specifies a list of modules that are imported.

The symbols defined in the definition module of the imported module are not directly visible in the importing module. A qualified reference is required to refer to those symbols.

A qualified reference takes the following form:

ModName.identifier

You can refer to all identifiers from the imported definition module in this way.

The second form of the import declaration imports specific symbols from a module.  When this form is used, the symbols named in the IdList are made visible without qualification.  Only the symbols named in the IdList are made available by this form of import.

You can use both forms of import to import the same module if you want some of the symbols to be visible without qualification and others only with qualification.  You might need to do this to avoid double declarations when two modules you import both define the same name.

You can import from the same module in multiple import declarations but you cannot import the same symbol more than once.

Local Modules

Local ModulesModulesLocalLocal modules are modules that are declared inside other modules.  Local modules provide a filter for symbols.  You can control which symbols from the environment are visible inside the module, and which symbols declared in the module are visible outside the module.

Local modules take the following form:

MODULE ModName [[Protection]];
{Import}
[Export]
{Declaration}
[ BEGIN
  ListOfStatements
[EXCEPT
  ListOfStatements]
[
 FINALLY
  ListOfStatements
 EXCEPT
  ListOfStatements
]
]
END ModName;

Imports in local modules

The import declaration takes the same form in local modules.  The symbols imported, however, are not necessarily symbols from other compilation units.

The symbols imported by a local module must be visible in the block containing the module.  Only those symbols imported and the standard pervasive identifiers are visible inside the module.

The export declaration

Normally, symbols declared in a module are not visible outside the module.  You can use an export declaration in the module to specify those symbols that are made visible in the containing block.

The export declaration takes the following form:

EXPORT [QUALIFIED] identifier {,identifier};

Each identifier must be visible in the module, either because it is defined in the module, or because it was exported from a nested module. These identifiers, and only these identifiers, are made visible in the block containing the local module declaration.

Because the identifiers on the export list are made visible in the containing block, they must not conflict with another identifier declared in that block or exported from another local module to that block.

If you use the QUALIFIED keyword, the identifiers are not made directly visible, they must be qualified by the module name.

Module Initialization

The ListOfStatements following the BEGIN keyword up to but not including the FINALLY keyword if present, in implementation and local modules is initialization code for the module.

The initialization code for all implementation modules is executed before the start of the main program.  The order of initialization cannot be guaranteed. However all modules that a module depend on are initialized before its own initialization code is executed.

If a local module is nested in a procedure, its initialization code is executed each time the procedure that contains the module is called.

If the module is local to an implementation module and not to any procedure, the initialization code is executed when the implementation module is initialized.

If the module is local to the program module, its initialization code is performed after all implementation modules and before the start of the program.

Local modules are initialized in the order that they are declared in the source file.

Module Termination

The ListOfStatements following the FINALLY keyword in implementation and local modules is termination code for the module.

The termination code for a module is not active, and will not be executed unless the initialization code completes with an unhandled exception.

The termination code for all implementation modules is executed when the program is terminated either with a call to the HALT procedure or after the last statement in the main program is executed. The order of termination is the exact reverse of the order of initialization.

If a local module is nested in a procedure, its termination code is executed each time the procedure that contains the modules finishes execution and returns to the procedure that called it.

If the module is local to an implementation module and not to any procedure, the termination code is executed when the implementation module termination code is finished executing.

Local modules are terminated in the reverse order that they are declared in the source file.

Exception handling in Module initialization and termination code

Both the initialization and termination sections can have their own unique exception handler. You should think of the BEGIN and FINALLY parts as two separate procedures.

Module Protection

Modules can have an associated protection.  The module protection value is a set constant of type PROTECTION in square brackets following the module name. The protection type is defined as

32-bit mode:

PROTECTION = PACKEDSET OF CARDINAL[0..31];

16-bit mode:

PROTECTION = PACKEDSET OF CARDINAL[0..15];

Note: the Stony Brook Modula-2 runtime system does not do anything with the module protection value.

The module protection is defined as an interrupt mask. Thus the code in a module can protect itself from certain interrupts. Two pervasive constants of type PROTECTION are defined. INTERRUPTABLE and UNINTERRUPTABLE. All values of type PROTECTION satisfy the following relationship.

INTERRUPTABLE <= AnyProtectionValue <= UNINTERRUPTABLE

The current protection value can be accessed via the COROUTINES.PROT function (see ISO module COROUTINES).


Source:

  • Stony Brook Modula-2 documentation. Used with permission. Note: Stony-Brook M2 offers an extended syntax with features not described here. Stony Brook M2 users are encouraged to visit the Stony Brook website and to consult the Stony Brook help system.