MAINSAIL Language Manual, Chapter 13

previous   next   top   contents   index   framed top   this page unframed


13. Intmods

Intmods are compiler symbol table files created by compiling MAINSAIL MODULEs. The symbols defined in intmods may be used in the compilation of other MODULEs or by MAINSAIL system programs like MAINDEBUG or the MAINSAIL disassembler.

An intmod may contain several kinds of information, including:

The outer symbols may be used by other MODULEs that open the intmod during compilation. The other contents of an intmod are for MAINSAIL system programs.

An intmod is generated (or updated) during a MODULE's compilation only when a compiler option is in effect that requires information to be stored in the intmod. By default, no intmod is generated. Any of the following options cause an intmod to be generated (or updated):

Option Information Stored in Intmod
ALIST instruction maps
DEBUG instruction maps, symbol tables, debug information
INCREMENTAL parse trees for INLINE PROCEDUREs, symbol tables
MONITOR instruction maps (needed by PERSTMT)
PERSTMT instruction maps
SAVEON parse trees for all PROCEDUREs, symbol tables

In each case, only information required by the options is put into the intmod.

The name “intmod” comes from “intermediate MODULE”, because the intmod format is intermediate between source code and object code.

13.1. Typical Use of Intmods

Intmods are typically used as repositories of symbols common to several MODULEs, a role played by “header” files in many languages.

In large programs, it is often the case that many declarations (such as MODULE interfaces, CLASS declarations, small INLINE PROCEDUREs for manipulating commonly used data structures, etc.) are used by a number of MODULEs. MAINSAIL requires that these declarations appear at the outer level of each MODULE, but it is impractical to maintain identical copies of these declarations in the source code for each MODULE (if a declaration is changed in one MODULE, it must be changed in all MODULEs; this is too big a task to be manageable in a truly large programming project).

The solution to the problem is to put the common declarations in a definitional MODULE, i.e., a MODULE whose only purpose is to be compiled to produce an intmod. MODULEs that require the declarations in the definitional MODULE then issue a directive to restore from (obtain the symbols from) the intmod produced from the definitional MODULE.

Example 13–1 shows three MODULEs, A, B, and C, whose interfaces and other common declarations are kept in a definitional MODULE D.

Example 13–1. Use of Intmods to Maintain Common Declarations
The MODULE D:

BEGIN "d"
$DIRECTIVE "NOOUTPUT"; SAVEON;
    # 
These directives tell the compiler to make
    # 
an intmod for D instead of an objmod
MODULE a (... A's interface...);
MODULE b (... B's interface...);
MODULE c (... C's interface...);
CLASS x (... CLASS used by ABand C...);
INLINE POINTER(xPROCEDURE newX;
... 
PROCEDURE used by ABand C for allocating
    
records of the CLASS x...
INLINE PROCEDURE disposeX (MODIFIES POINTER(xp);
... 
PROCEDURE used by ABand C for disposing
    
x records...
END "d"

The MODULE A:

BEGIN "a"
RESTOREFROM "d"; # Obtain declarations from D
... 
make calls to B and Cuse xcall newX
    
and disposeX...
END "a"

The MODULE B:

BEGIN "b"
RESTOREFROM "d"; # Obtain declarations from D
... 
make calls to A and Cuse xcall newX
    
and disposeX...
END "b"

The MODULE C:

BEGIN "c"
RESTOREFROM "d"; # Obtain declarations from D
... 
make calls to A and Buse xcall newX
    
and disposeX...
END "c"

13.2. Intmod Directives

Several directives are provided for dealing with intmods:

$DIRECTIVE "OPENMODULE s";
$DIRECTIVE "MAKEMODULEVISIBLE m1 ... mn";
$DIRECTIVE "MAKEMODULENOTVISIBLE m1 ... mn";
$DIRECTIVE "MAKEMODULEALLVISIBLE m1 ... mn";
$DIRECTIVE "MAKEVISIBLE s1,...,sn";
$DIRECTIVE "MAKENOTVISIBLE s1,...,sn";
RESTOREFROM "s";
SAVEON "s";

13.2.1. Opening Intmods and Accessing Symbols

In the $DIRECTIVE directive OPENMODULE s, s is the name of a MODULE or intmod file; the intmod must have been made with the SAVEON option in effect. The named intmod is opened, along with any supporting MODULEs, i.e., intmods that were open when the SAVEON that created the intmod was done.

13.2.2. MODULE Visibility

The $DIRECTIVE directive MAKEMODULEVISIBLE makes identifiers in one or more open MODULEs visible, i.e., accessible without the need for qualification by the MODULE-name-and-dollar-sign prefix (the MODULEs themselves are also said to be visible). The mi are MODULE names (never file names). Each MODULE is opened, if not already open, as by OPENMODULE (so each intmod must have been made with the SAVEON option in effect). The identifiers in a visible MODULE can still be qualified, if desired. The effect of making a MODULE visible can be undone by the $DIRECTIVE directive MAKEMODULENOTVISIBLE. These two directives can be used any number of times for the same MODULE, alternately making it visible, then invisible.

13.2.3. Qualified Identifiers: Low-Level Access to Identifiers from Intmods

Identifiers from intmods may be specified using the syntax:

intmodModuleName$identifier

For example, an identifier id from an intmod FOO is specified using the compound, or qualified, identifier foo$id. foo$id refers to the id in the intmod FOO, even if there is also an id in the current MODULE or in another open intmod. The compound identifier is considered a single identifier for purposes of macro substitution; i.e., if a macro bar has been defined, or if bar is a macro parameter, its definition is not expanded in bar$id or mod$bar. Whenever a compound identifier is found, the compiler automatically opens the specified MODULE if it is not already open; e.g., if baz$xxx is encountered and BAZ is not an open MODULE, the compiler acts as if it had seen:

$DIRECTIVE "OPENMODULE baz";

This method of using identifiers from an intmod works whether or not the intmod itself has been made visible, and whether or not the individual identifier in the intmod has been made visible. Use of qualified identifiers can therefore go beneath the directives that specify visibility; for this reason, this use of identifiers from an intmod is “lower level” and less commonly used than issuing the RESTOREFROM directive (see below) and using an unqualified identifier name.

13.2.3.1. Example of the Use of Qualified Identifiers to Distinguish between Symbols from Two Different Open Intmods
The following code twoIntmods) shows how the qualified identifiers b$i and c$i are used to distinguish between two different identifiers i:

BEGIN "a"

RESTOREFROM "b";
RESTOREFROM "c";

INITIAL PROCEDURE;
write(logFile,b$i,eol,c$i,eol);

END "a"

BEGIN "b"

$DIRECTIVE "NOOUTPUT";
SAVEON;

DEFINE i = 2;

END "b"

BEGIN "c"

$DIRECTIVE "NOOUTPUT";
SAVEON;

DEFINE i = 3;

END "c"

13.2.4. Individual Symbol Visibility

The individual symbol visibility directives provide a way to control the visibility of individual symbols in an intmod. These directives are useful only if the intmod is saved. Only those symbols individually visible are made visible in another MODULE by MAKEMODULEVISIBLE.

Each intmod has a visibility default that can have one of the two values visible and invisible. At the start of compilation a MODULE's visibility default is initialized to visible. This default applies to all symbols not explicitly marked by MAKEVISIBLE or MAKENOTVISIBLE.

The $DIRECTIVE directive MAKEVISIBLE s1,...,sn does the following:

The $DIRECTIVE directive MAKENOTVISIBLE s1,...,sn does the following:

These directives are not available as compiler subcommands.

The symbols si may be the names of identifiers declared or defined anywhere in the outer block of the current MODULE. An si may also be of the form className.field or moduleName.[field], assuming the field field is declared in the current MODULE. There is no way to affect the visibility of individual symbols from other intmods.

The si are not processed until the compiler has read the entire MODULE, so the si may refer to identifiers declared after the visibility directive. A visibility directive can appear in a PROCEDURE body, but it cannot refer to symbols local to the PROCEDURE.

After the compiler has read the entire MODULE, each referenced symbol on the visibility list is “marked”, so that the intmod contains the marked symbols. The visibility default is stored in the intmod.

When an intmod is made visible, only the following outer symbols are actually visible:

A symbol can always be used with a compound identifier, e.g., modNam$symbolName, even if symbolName is not visible.

All symbols in the current MODULE are always visible within the MODULE, regardless of the symbol visibility directives.

If a symbol x from an intmod B is visible in a MODULE A, then A cannot declare or define x without a MODULE prefix, since the definition from B would conflict. However, x may be declared in A as a$x. If you wish to redeclare a predefined identifier, e.g., integerCode, in a MODULE FOO, you must use the form foo$integerCode. After the point of definition in FOO, integerCode refers to foo$integerCode, not the system macro integerCode. Specifically, the macro definition looks something like:

DEFINE foo$integerCode = 7L;

13.3. RESTOREFROM and SAVEON

The directive:

RESTOREFROM "s";

where s is a MODULE name or file name, first performs:

$DIRECTIVE "OPENMODULE s","MAKEMODULEVISIBLE m";

where m is the name of the MODULE in the intmod specified by s. In addition, all MODULEs that were open or visible when the SAVEON for m occurred (supporting MODULEs for m) are also made open or visible, i.e., are restored to their status at the time of the SAVEON; see Section 13.4. By contrast, MAKEMODULEVISIBLE makes only the specified MODULEs visible; it makes sure that MODULEs that were open when the specified intmods were made are open, but does not make them visible, regardless of whether or not they were visible when the intmod was made.

The directive:

SAVEON "s";

makes an intmod with the SAVEON option in effect for the current MODULE, when the compilation is complete (saving a partial MODULE is not possible). The intmod contains all the information required to support the OPENMODULE directive. s is the name of the file on which the SAVEON is stored. s may be omitted, i.e.:

SAVEON;

in which case a default file name is used, based on the name of the MODULE being compiled (or the intmod is put in an intmod library if the appropriate compiler subcommands are in effect; see Section 4.25 of the MAINSAIL Compiler User's Guide for details).

The compiler does not permit an intmod to be opened unless it was created with the SAVEON option in effect. An intmod created in the absence of SAVEON does not contain enough information to support its use by the compiler, although it may be usable by other tools such as the debugger and disassembler.

Typically, a SAVEON directive occurs in a “definitional MODULE”, one for which no code is generated and which is used only as a repository of definitions and declarations. The MAINSAIL language does not make a distinction between the syntax of a definitional MODULE and that of an “executable MODULE”, one for which code is generated and executed. The source code for an executable MODULE can theoretically serve as a definitional MODULE, or vice versa, although it is rare in practice to write a MODULE that plays both roles.

13.4. Visibility from Supporting Intmods When a RESTOREFROM Is Done

If a MODULE A does a RESTOREFROM from an intmod B, symbols from an intmod C used by B have the same visibility as they had at the end of the compilation of B, regardless of any individual symbol directives in B. For example, if all symbols in C are visible at the end of the compilation of B, and B is currently visible in A, then all symbols in C are currently visible in A. This is true regardless of the status of B's visibility list, e.g., even if no symbols from B are visible.

13.5. MAKEMODULEALLVISIBLE

The $DIRECTIVE directive MAKEMODULEALLVISIBLE has the syntax:

$DIRECTIVE "MAKEMODULEALLVISIBLE m1,m2, ... mn";

It makes all the symbols in the open MODULEs mi visible, regardless of any individual symbol visibility directives given in the MODULEs (unlike MAKEMODULEVISIBLE, which makes visible only those individual symbols that are made visible by the MODULEs). This is useful if you want to make an intmod that has private and public symbols. The public symbols are visible to all MODULEs that issue a RESTOREFROM on the MODULE, and both the public and private become visible if you then also give the above directive.

It is often the case that an intmod for a particular project is used by a group of MODULEs that implement the project and also by other MODULEs that are “consumers” of the project. The consumer MODULEs typically do not need to know as much information about the contents of the intmod as do the implementer MODULEs. The consumer MODULEs should therefore do just a RESTOREFROM from the project's intmod; the implementer MODULEs can do a RESTOREFROM followed by a MAKEMODULEALLVISIBLE, e.g.:

RESTOREFROM "projectIntmod";
$DIRECTIVE "MAKEMODULEALLVISIBLE projectIntmod";

13.6. Unqualified Identifier Search Rules

The MAINSAIL compiler searches for an unqualified identifier in the following order:

  1. It searches the MAINSAIL keywords.

  2. It searches the symbols defined in the current PROCEDURE, if compiling a PROCEDURE body.

  3. It searches the outer identifiers of the current MODULE.

  4. It searches visible intmods, in the order most recently made visible to least recently made visible.

  5. It searches the global symbol table (in which symbols defined by $GLOBALREDEFINE reside).

The first identifier found by searching in the above order is the one used by the compiler. No warning is given if the same identifier occurs in another open intmod. Compilation may become slower as more intmods are made visible, since there are more symbol tables to be searched. For this reason, it may be a good idea to keep as many identifiers as possible in a single intmod, if the identifiers are to be used without qualification. There is no compilation slow-down if several intmods are opened but not made visible, and all references to identifiers in the intmods are qualified; however, this puts the burden on the user of remembering each identifier's declaring MODULE, which may not be convenient.

13.7. Use of Symbols from an Intmod

Interface PROCEDUREs and variables from an open intmod are processed as intermodule references, as usual. Referenced non-interface PROCEDUREs and outer variables from an open intmod are “copied” out of the intmod; i.e., the PROCEDUREs are compiled into the current MODULE as if they were FORWARD PROCEDUREs, and the outer variables are treated as if they were declared as outers in the current MODULE. Macros from an open intmod are expanded in the usual way.

Note that referenced outer symbols from an intmod are treated as outers of the current MODULE, never as symbols local to a PROCEDURE, even the intmod directive that made the symbols visible was within a PROCEDURE.

If non-interface PROCEDUREs and outer variables from an intmod are not referenced in the current MODULE, then they are not compiled into the current MODULE; i.e., if a MODULE does not use a symbol from an intmod, then no code is generated or space allocated for that symbol.

PROCEDUREs called by PROCEDUREs copied into the current MODULE are also copied into the current MODULE. The compiler remembers the MODULE in which it found each PROCEDURE, so that the PROCEDUREs that would have been called at the point of compilation in the original intmod are called, not PROCEDUREs of the same name in the current MODULE; for example, if a MODULE A contains:

BEGIN "a"

SAVEON;

PROCEDURE p1;
body for A's p1

PROCEDURE p2;
BEGIN ... p1; ... END;

END "a"

and a MODULE B contains:

BEGIN "b"

RESTOREFROM "a";

PROCEDURE b$p1;
    # 
Need to specify b$p1 to avoid conflict with
    # 
p1 obtained from a
body for B's p1

INITIAL PROCEDURE;
BEGIN ... p2; ... END;

END "b"

then the call to p2 in B's INITIAL PROCEDURE calls the PROCEDURE p2 copied from A, i.e., a$p2, which calls the PROCEDURE p1 copied from A, i.e., a$p1, rather than the p1 in B, since a$p1, not b$p1, would have been called at the point where p2's body was encountered when it was compiled (in MODULE A).

PROCEDUREs extracted from definitional MODULEs are more quickly compiled than FORWARD PROCEDUREs, since the source text has already been parsed and converted into the compiler's internal representation.

13.7.1. Redefining Macros from Intmods

It is permissible to use REDEFINE to change the definition of a macro from an intmod; i.e.:

REDEFINE foo$bar = ...

is permissible outside of FOO. Subsequently, within the current compilation, the compiler treats foo$bar as having the new value.

This kind of redefinition is permissible even when bar was not originally declared in FOO at all.

However, it is not permissible to declare an outer variable of FOO outside of FOO:

INTEGER foo$i; # ERROR

13.7.2. CLASSes Declared in Different Intmods Are Different CLASSes

Consider the following three short MODULEs:

BEGIN "foo1"
$DIRECTIVE "SAVEON","NOGENCODE";
CLASS c (...);
MODULE e (PROCEDURE d (POINTER(cp));
PROCEDURE d (POINTER(cp);;
END "foo1"

BEGIN "foo2"
$DIRECTIVE "SAVEON","NOGENCODE";
CLASS c (...);
MODULE e (PROCEDURE d (POINTER(cp));
END "foo2"

BEGIN "foo3"
RESTOREFROM "foo1";
RESTOREFROM "foo2";
POINTER(cq;   # this "cis foo2$csince FOO2
                # 
was restored from more recently
INITIAL PROCEDURE;
d(q);
END "foo3"

FOO3 includes the definitions of the CLASS c when it restores from FOO1 and FOO2. However, the CLASSes named c declared in those two MODULEs are really two CLASSes: foo1$c and foo2$c.

In FOO3, d(q) is compiled as foo1$d(q) rather than e.d(q), since outers take precedence over fields. foo1$d's parameter p is classified with foo1$c, whereas q is classified with foo2$c. Since foo1$c and foo2$c come from different declarations, they are not the same CLASS. Hence, the compiler complains of an argument mismatch when compiling FOO3.

There are two simple ways to eliminate the error from this code:

Neither of these simple changes is ideal, since the fundamental problem is the duplicate declarations of c and e in FOO1 and FOO2. A better change is to move the duplicate declarations into a third common MODULE which is restored from by FOO1 and FOO2.

13.8. Intmod Search Rules

When searching for an intmod, MAINSAIL looks in the specified file name, if processing a compiler directive that specifies a file name. If processing a directive that specifies a MODULE name, or if looking for an intmod for some program other than the compiler, it follows the search rules described in Section 14.2. If unsuccessful, it tries treating the given name as a file name instead of a MODULE name, and attempts to open the named file. If it still does not find the intmod, and the MODULE it is looking for is a supporting MODULE (a MODULE used during compilation by some other MODULE), it last attempts to find the intmod file under the file name specified when it was used during compilation (which may be different from the file name actually used, e.g., if a logical file name or searchpath was in effect).

13.9. Changing an Intmod

In many cases, if an intmod is remade, all intmods that reference it must also be remade; otherwise, undefined errors may result when the changed intmod is opened. The exceptions to this rule are described below in
Section 13.9.1.

If the date on an intmod is older than the date on one of its supporting intmods, a warning message is issued when the supporting intmod is opened. If the date on the supporting intmod file is wrong (e.g., if the file has been copied, or the system clock is inaccurate), but the contents are correct, the supporting intmod should be used; but if the supporting intmod actually has been changed in a way that is not innocuous since it was used in the compilation of the first intmod or objmod, undefined errors may result.

13.9.1. Innocuous Intmod Changes

Certain kinds of innocuous changes to intmods do not necessarily require recompilation of other intmods and MODULEs that rely on the intmods that are changed. This means, for example, that if an intmod A is used by an intmod B, and A is later changed in a way that does not affect B, the compiler would be able to use an old version of B with a newer version of A without blowing up.

Additions of new declarations and definitions to an intmod A are considered innocuous to an intmod B, since declarations and definitions added to A since the last time B was compiled obviously cannot affect B. Any changes to existing definitions or declarations that might be used by B, however, are not innocuous; in particular, the following changes are not innocuous:

The compiler is unable to detect automatically when an intmod change is innocuous. Thus, its default action is to complain about incompatible intmods by issuing a warning message. You should ignore the warning only if you are certain that only innocuous intmod changes were made. If a non-innocuous change is made, e.g., if a field is added in the middle of an existing CLASS or MODULE, then all intmods that rely on the changed intmod must be recompiled; however, it is your responsibility to determine when this is necessary.

13.10. Intmods vs. the SOURCEFILE Directive

An intmod may be used as an alternative to “header” files, i.e., sourcefiled files containing declarations common to several MODULEs (see Section 16.2). Declarations from intmods are processed more quickly by the compiler than sourcefiled header files.

Suppose that several MODULEs each require three “header” files hdr1, hdr2, and hdr3 as sourcefiles. Each of the MODULEs could use the SOURCEFILE directive to obtain the information in the header files, with each header file being recompiled. But it would be more efficient (assuming the header files are not being changed) to compile the header files once and then save the state of the symbol table. Each MODULE could then restore the symbols from the saved intmod, thereby giving the effect of having just compiled the header files.

A convenient way to create the saved file is to compile this file:

BEGIN "hdr"
SAVEON; # Make intmod
$DIRECTIVE "NOOUTPUT"; # Don't need objmod
SOURCEFILE "hdr1";
SOURCEFILE "hdr2";
SOURCEFILE "hdr3";
END "hdr"

Each of the MODULEs would then be written as:

BEGIN "modNam"
RESTOREFROM "hdr";
...
END "modNam"

previous   next   top   contents   index   framed top   this page unframed

MAINSAIL Language Manual, Chapter 13