2.2.4 Modules and exports

As we have seen, each Algol 68 source file contains either a particular program or a prelude packet. Prelude packets consist on the definition of a single top-level module, that is itself identified by a module indicant.

Consider for example a source file called trilean.a68 that implements strong Kleene three-valued (“trilean”) logic. It does so by the mean of a definition module called Trilean. A sketch of such a file may look like this:

module Trilean =
def
    pub mode Tril = int;

    pub Tril dontknow = 0, yes = 1, no = 2;

    pub prio AND3 = 3, OR3 = 3, XOR3 = 3;

    pub op NOT3 = (Tril a) Tril:
       (a + 1 | dontknow, no, yes);

    C ... other definitions ... C
fed

The module indicant Trilean identifies the module. If we now compile this file to an object file using GCC:

$ ga68 -c trilean.a68

The result of the compilation is an object file trilean.o, plus some exports information which is placed in a section in the object file, named .a68_exports. The exports information describes the interface provided by the Trilean module defined in the compilation unit. This includes all the modes, identifiers, priorities, operators, etc, that are publicized by the module. The particular encoding used to hold these exports is highly compact and not easy readable by persons; instead, it is designed to be read back by GCC when it builds another compilation unit that, in turn, needs to access the Trilean module.

Consider the following sketched particular program that resides in a source file main.a68, and that uses trilean logic:

access Trilean
begin
      C ... C
end

When this program gets compiled by GCC using ga68 -c program.a68, the compiler finds the access clause and needs to locate some exports for the module Trilean. To do so, it searches in the modules search path, composed by the current working directory, some system directories and all directories specified in -I and -L options, looking for files called trilean.m68, trilean.so, trilean.a and trilean.o, in that order, where:

trilean.m68

Is a stand-alone file expected to contain export data for one or more modules.

trilean.so

Is a DSO, or shared object, expected to contain a .a68_exports section with exports data for one or more modules.

trilean.a

Is an archive, whose constituent object files may contain .a68_exports sections with exports data for one or more modules.

trilean.o

Is an object file expected to contain export data for one or more modules in a .a68_exports section.

The files are tried in order, and if export data for the requested module Trilean is found, it is read in, decoded, and used to compile main.a68 into main.o.

The last step in obtaining an executable for our program would be to use GCC to do a link like ga68 trilean.o main.o -o main.

Module indicants such as Trilean are bold words in the language. This means that, independently of the stropping regime used, they are constituted by a bold letter followed by a sequence of zero or more bold letters and digits. Using the modern stropping supported by the GNU Algol 68 compiler, this means that all of Trilean, TRILEAN and Tri_lean denote exactly the same module indicant, trilean.

The mapping from module indicant to the “base name” used to locate the module exports is quite straightforward: the bold letters are transformed to lower-case letters, and the bold digits are just normal digits. Therefore, the exports for the module GRAMP_Grammar would looked in files grampgrammar.m68, libgrampgrammar.so, etc.

But often this default, straightforward mapping, is not what we need.

Suppose for example that a shared object installed in the system, liba68goodies.so, provides many facilities in the form of several modules, including a Trilean module. We want to use the trilean implementation of liba68goodies in our program main.a68. If we just access Trilean GCC will look for trilean.m68 etc, but wont even consider looking in liba68goodies.so. Accessing A68Goodies is obviously not a solution, as the module we want is Trilean and there may not even be a module called A68Goodies in liba68goodies.so.

The solution for this is to use the modules map of the compiler. This map is an association or map between module indicants and base-names. When it comes to access some module, the compiler looks in the map. If there is an entry for the module’s indicant, then it fetches the base-name to use for looking for the module’s export data. If there is not an entry for the module’s indicant then the default, straightforward mapping described above is attempted.

By default the map is empty, but we can add entries by using the -fmodules-map= and -fmodules-map-file= command-line options. The first option expects entries to be added to the map in a string in the command-line, whereas the second option expects the name of a file containing the entries to add to the map. In both cases the format describing the entries is exactly the same (see Module search options).

In our case, we could compile our main program specifying an entry in the map telling the compiler where to find the trilean and logging modules:

$ ga68 -fmodules-map="a68goodies=Trilean,Logger" -c main.a68