previous next top contents index framed top this page unframed
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.
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 A:
The MODULE B:
The MODULE C:
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.
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 A, B, and C...);
INLINE POINTER(x) PROCEDURE newX;
... PROCEDURE used by A, B, and C for allocating
records of the CLASS x...
INLINE PROCEDURE disposeX (MODIFIES POINTER(x) p);
... PROCEDURE used by A, B, and C for disposing
x records...
END "d"
BEGIN "a"
RESTOREFROM "d"; # Obtain declarations from D
... make calls to B and C; use x; call newX
and disposeX...
END "a"
BEGIN "b"
RESTOREFROM "d"; # Obtain declarations from D
... make calls to A and C; use x; call newX
and disposeX...
END "b"
BEGIN "c"
RESTOREFROM "d"; # Obtain declarations from D
... make calls to A and B; use x; call 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;
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";
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.
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:
and a MODULE B contains:
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.
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:
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.
BEGIN "a"
SAVEON;
PROCEDURE p1;
body for A's p1
PROCEDURE p2;
BEGIN ... p1; ... END;
END "a"
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"
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 = ...
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(c) p));
PROCEDURE d (POINTER(c) p);;
END "foo1"
BEGIN "foo2"
$DIRECTIVE "SAVEON","NOGENCODE";
CLASS c (...);
MODULE e (PROCEDURE d (POINTER(c) p));
END "foo2"
BEGIN "foo3"
RESTOREFROM "foo1";
RESTOREFROM "foo2";
POINTER(c) q; # this "c" is foo2$c, since 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"