previous next top complete contents complete index framed top this page unframed
MODULEs communicate at runtime through interface fields, which are the variables and PROCEDUREs of each MODULE declared by the programmer to be accessible from other MODULEs.
A MODULE is written in the general form shown in Figure 11–1. The outer declarations sections may be empty; see Chapter 6. modNam is the name of the MODULE, enclosed in double quotes, and must be a valid MAINSAIL identifier. The name is used in the declaration of the MODULE, if the MODULE is explicitly declared (see Section 11.2). Since the runtime system uses a MODULE's name to identify it, every MODULE in a program must have a unique name. The programmer's choice of MODULE names must not conflict with the names of standard runtime MODULEs (see Appendix K).
Figure 11–1. A MAINSAIL MODULE
| BEGIN "modNam" outer declarations 1 PROCEDURE 1 outer declarations 2 PROCEDURE 2 . . . outer declarations n PROCEDURE n outer declarations n + 1 END "modNam" |
Since the compiler forms the output file intmod and objmod name from the MODULE name, a long MODULE name could result in a file name which is too long to be legal on some operating systems. This problem does not arise if the intmod and objmod are put into a library. It is the programmer's responsibility to deal with this situation if it arises.
The outer declarations declare all identifiers (except those predefined by MAINSAIL and those obtained from intmods; see Chapter 13) that are to be accessible from the point of declaration to the end of the MODULE, but not from any other MODULEs. The outer declarations of a MODULE m must include declarations of all MODULEs referenced by m. In addition, m must declare itself if it has any interface fields. MODULE declarations are described in Section 11.2.
A MODULE cannot contain another MODULE; i.e., MODULEs cannot be nested. All MODULEs are on equal footing; there is no explicitly declared “main” or “controlling” MODULE, although the MODULE first given control during a particular execution might be considered the “main” MODULE for that execution.
The division of a program into MODULEs is entirely up to the programmer. MAINSAIL has no portable rule by which to determine when a MODULE is too large, but each machine on which MAINSAIL is implemented may place an upper limit on the size of a MODULE it can execute. If such a limit exists, it may be found in the relevant part of the MAINSAIL System-Specific User's Guides.
Unlike many programming languages, MAINSAIL does not use a “link” step before execution. Instead, the MODULEs are brought into memory as needed during execution. The MAINSAIL runtime system provides all the facilities for intermodule communication. The programmer need never specify beforehand what MODULEs make up a program; a program is an open-ended collection of MODULEs whose identity is not determined until execution time. The dynamic binding of MODULEs provides a degree of flexibility lacking in statically linked systems.
The MAINSAIL runtime system automatically verifies that MODULE interfaces are consistent with one another when linkage between MODULEs is established; see Section 11.10.
11.1. Bound and Nonbound Data Sections
OWN variables (including outer and interface variables)
of a MODULE are stored in a data
structure called a data section.
After an object module (the compiled form of a MODULE) has been
brought into memory for execution,
it is referred to as a control section.
Each control section may be associated with at most one
bound data section and zero or
more nonbound data sections.
Bound data sections are created by means of the system PROCEDURE
bind; nonbound data sections are created by several forms of the
system PROCEDURE new.
Bound and nonbound data sections are identical in format,
but the interface fields of a bound MODULE can be accessed
indirectly (i.e., without using an explicit POINTER)
as described below.
Data sections differ from dynamic records in that each data section is associated with a control section. A PROCEDURE field may be accessed by means of a POINTER to a data section, but not by means of a POINTER to a record; MODULEs may contain PROCEDUREs, but records may not.
Interface fields associated with
bound data sections may be accessed by means of implicit MODULE
pointers; such access is called indirect access.
Interface fields associated with either bound or nonbound
data sections may be accessed by means of explicitly declared
POINTERs
to the data sections; such access is called direct access.
11.2. MODULE Declaration
MODULEs communicate by means of interface fields,
i.e., those variables and
PROCEDUREs specified by the programmer
(in MODULE declarations) as
accessible by other MODULEs.
Interface data fields are called interface variables
and are OWN variables (see Section 6.4); i.e., they reside in the
data section of the MODULE (see Section 11.6).
Unless a variable or PROCEDURE in a MODULE is explicitly declared as an interface field, it is not accessible from other MODULEs.
The outer declarations of a MODULE m must include, in any order (except that interface variables must be declared before use, and interface PROCEDUREs must be declared as interface PROCEDUREs before their bodies appear), declarations for all MODULEs of which interface fields are indirectly accessed by m (such declarations may, of course, be obtained from an intmod used by m instead of explicitly appearing in the text of m). In addition, m itself must be declared if it has any interface fields, even if they are not referenced within m, because the compiler must know the interface fields of the MODULE it is compiling. If m has no interface fields, its declaration may be omitted, in which case the compiler declares it automatically as a MODULE with no interface fields. That is:
BEGIN "m"
outer declarations # not including one for m
...
END "m"
is equivalent to:
BEGIN "m"
outer declarations
...
MODULE m; # default interface: no interface variables
# or PROCEDUREs
END "m"
The most common form of a MODULE declaration is:
MODULE v (declarations of interface fields)
where v is the MODULE's name. Interface fields may be variables and/or PROCEDUREs, in any order. The declaration of an interface PROCEDURE gives only its header. It serves as a FORWARD declaration (see Sections 7.13 and 16.22) for the PROCEDURE; i.e., the PROCEDURE may be called within v before its body has been seen. The PROCEDURE body must be given within the MODULE v, where the PROCEDURE is declared as usual. Interface variables of v are declared only in v's MODULE declaration; they are not redeclared in v after the MODULE declaration has been seen.
A sample MODULE declaration is shown in Example 11–2. The sample declares a MODULE named parse with several interface fields.
Example 11–2. Sample MODULE Declaration
| MODULE parse ( INTEGER val,lineNum,index; STRING token,line; INTEGER ARRAY(1 TO 10) order; PROCEDURE getToken; STRING PROCEDURE msgTxt (INTEGER page,msgNum); ) |
A MODULE m that accesses fields of a MODULE n need declare only a prefix of n's interface if it does not access all of n's fields. No error occurs (see Section 11.10), provided that m's view of n matches a prefix of n's view of itself. For example, if the MODULE n has two fields, and is declared in n as:
MODULE n (INTEGER field1; PROCEDURE field2);
and the MODULE m uses only the first field (field1) of n, then n may be declared in m as:
MODULE n (INTEGER field1);
The term “field variable” is used for both interface PROCEDUREs and interface variables.
For every MODULE m declared by means of the keyword MODULE in a MODULE n (and actually used in n), n maintains a hidden implicit MODULE pointer to m's bound data section. It is this implicit MODULE pointer that is used in indirect access to interface fields of m.
For example, if m1 is declared as:
MODULE m1 (STRING name,quest; INTEGER val; PROCEDURE findVal)
then the interface fields of m1 can be indirectly accessed as the field variables m1.name, m1.quest, m1.val, and m1.findVal. The interface variables of m1 may be altered by other MODULEs; i.e., there are no “protected” or “read-only” fields.
m.f can be written as f if m is the current MODULE or if m is the only MODULE declared in the current MODULE with a field named f. In either case, the compiler effectively provides the m. prefix. The compiler generates the same code regardless of whether or not the abbreviated form is used (i.e., there is no efficiency penalty or advantage for including the m.).
As an example, many of the MAINSAIL system
PROCEDUREs
are interface fields of various system MODULEs. The
programmer need not know the names of the runtime MODULEs.
To call a system PROCEDURE, the programmer specifies only
the PROCEDURE name. All the system PROCEDUREs have been
given unique names, and hence the compiler can figure out the
MODULE
name. If the programmer declares an interface variable with the same
name as a system PROCEDURE, the name of the system PROCEDURE
alone may no longer suffice to produce an unambiguous reference.
System PROCEDURE names must not be explicitly
prefixed with their MODULE names, since XIDAK reserves the right
to change the MODULE in which a system PROCEDURE resides.
11.4. CLASSes with PROCEDUREs and
CLASSes as Prefixes of MODULEs
The fields of a MODULE can be supplied using a CLASS,
with a declaration such as:
MODULE(c) m
where c is a CLASS name. The MODULE declaration may declare the MODULE to be of a prefixed CLASS by adding extra fields to an existing CLASS, in the form:
MODULE(c) m (declarations of additional fields)
The declaration of the MODULE PARSE of Example 11–3 is equivalent to the declaration of Example 11–2.
Example 11–3. Sample MODULE Declaration Using a CLASS
| CLASS parseCls ( INTEGER val,lineNum,index; STRING token,line; INTEGER ARRAY(1 TO 10) order; PROCEDURE getToken; STRING PROCEDURE msgTxt (INTEGER page,msgNum); ); MODULE(parseCls) parse; |
Both bound and nonbound data sections may be directly accessed.
The system PROCEDURE bind and some forms of the system PROCEDURE new allocate a data section instead of a dynamic record and return a POINTER to the allocated data section. These forms specify the name of the MODULE for which a data section is to be created.
Figure 11–4 shows examples of data section allocations and field variables.
Figure 11–4. Accessing Data Section Fields with a POINTER
CLASS parseCls ( If there exists a MODULE PARSE of parseCls, p may be made to point to the bound data section of PARSE by means of:
p := bind("PARSE"); In addition, if PARSE is declared in a MODULE declaration such as:
MODULE(parseCls) parse; then PARSE can also be bound using:
p := bind(parse); The call to bind allocates the bound data section if it has not already been allocated. p may be made to point to a nonbound data section of PARSE by means of:
p := new("PARSE"); or, if PARSE is declared in a MODULE declaration:
p := new(parse); The call to new always allocates a new nonbound data section. If p is made to point to a data section of the CLASS parseCls, the following directly accessed field variables are valid:
p.val |
When a data section is disposed (see Section 33.30), MAINSAIL automatically invokes the MODULE's FINAL PROCEDURE, if there is one (see Section 11.12). When a MODULE is disposed, MAINSAIL disposes all the data sections for that MODULE, then releases the control section for the MODULE (so that if a new data section is allocated for the MODULE, the search for the MODULE's control section proceeds from scratch as described in Section 14.2). At the end of a MAINSAIL execution, MAINSAIL normally executes the FINAL PROCEDUREs associated with all data sections and then closes any open files.
The exception $disposedDataSecExcpt is raised when an attempt is made to return to a PROCEDURE invocation associated with a disposed data section.
When a MODULE is unbound
(see Section 49.1), the bound data section is disposed, but
the control section is not released.
11.7. Allocation of a Dynamic Record of a CLASS with
PROCEDURE Fields
Allocation of a dynamic record of a CLASS c by means of new(c),
where c
contains PROCEDURE fields, results in a record in which the
PROCEDURE
fields are not valid;
the compiler issues a warning message if such an allocation is
performed.
The effect of calling a PROCEDURE in c using such a record is
undefined.
PROCEDURE fields may be accessed with
the POINTER only if a data section of the CLASS is allocated
instead of a record.
11.8. Garbage Collection of Data Sections
Nonbound data sections are subject to garbage collection, like
dynamic records and ARRAYs.
Bound data sections are not garbage collected;
once created, they continue to exist until explicitly unbound or
until the MAINSAIL execution finishes.
11.9. Establishing MODULE Linkage
A MODULE m may at any time indirectly
call an interface PROCEDURE in another
MODULE n.
However, m can indirectly access an
interface variable in n only if a bound data section already exists
for n.
When a bound data section for n
is created, implicit MODULE pointers
to n in all MODULEs that use n are initialized.
If a MODULE m is allocated and a bound data section for n
exists,
m's implicit MODULE pointer to n is initialized.
When the implicit MODULE pointer from m to n is initialized,
m is said to have linkage to n.
If checking is in effect, MAINSAIL raises the exception $unboundModuleExcpt if an attempt is made to access an interface variable of a MODULE to which linkage is not yet established (i.e., for which no bound data section exists).
In the future, MAINSAIL may support automatic
establishment of linkage upon interface data access; this has not
been done so far because of execution overhead considerations.
11.10. Intermodule Consistency Checking
If a MODULE A indirectly accesses a MODULE B,
the compiler requires that A include a MODULE declaration for
B and that B include a MODULE declaration for
itself. When A gets linkage to B,
MAINSAIL checks these MODULE
declarations for consistency. A MODULE inconsistency is
detected if A's view of B is not a prefix of
B's view of itself.
If the interfaces are inconsistent, an error occurs.
The consistency check verifies that the declarations of corresponding interface variables and PROCEDUREs are compatible. Corresponding interface variables must have the same data type; if the variables are ARRAYs, they must have the same bounds. Corresponding interface PROCEDUREs must have equivalent declarations; corresponding parameters must be of the same data type, and must be both USES, both MODIFIES, both PRODUCES, or both $REFERENCE. It is not checked that corresponding identifiers have the same name. Currently, it is also not checked that corresponding POINTERs have the same CLASS. Such checks may be added in future versions of MAINSAIL.
For example, assume the
MODULE U contains (or sourcefiles, or restores
from an intmod that includes):
Then if P contains a declaration like:
that violates rule #1 above (P does not provide everything
U knows about; specifically, it does not provide proc).
If P contains a declaration like:
rule #2 is violated (the data type of i
does not match in P and U).
If P contains a declaration like:
that also violates rule #2 (the second parameter of proc
is declared as
a MODIFIES in U but not in P).
If P contains a declaration like:
that violates rule #3 (s and i
are in a different order in P's and U's
declarations).
However, it is permissible under rule #4 for P
to contain a declaration like:
The field ptr is not declared in U's view of P,
but it appears in the
interface after every field that U
knows about, so there is no problem.
11.10.1. Detailed Intermodule Consistency Checking Rules, with an
Example
The complete rules for interface inconsistency are these,
given a MODULE P
(the producer) that provides an interface, and a MODULE U (the user)
that accesses PROCEDUREs or variables in P:
MODULE p (
INTEGER i;
STRING s;
PROCEDURE proc (REAL r; MODIFIES BITS b);
);
MODULE p (
INTEGER i;
STRING s;
);
MODULE p (
LONG INTEGER i;
STRING s;
PROCEDURE proc (REAL r; MODIFIES BITS b);
);
MODULE p (
INTEGER i;
STRING s;
PROCEDURE proc (REAL r; BITS b);
);
MODULE p (
STRING s;
INTEGER i;
PROCEDURE proc (REAL r; MODIFIES BITS b);
);
MODULE p (
INTEGER i;
STRING s;
PROCEDURE proc (REAL r; MODIFIES BITS b);
POINTER(xyz) ptr;
);
11.11. INITIAL PROCEDURE
Each MODULE may contain a single typeless and parameterless
PROCEDURE that is to be called whenever a data section for
the MODULE is allocated.
This
PROCEDURE is qualified with INITIAL,
in accordance with the syntax
described in Section 7.11.
The INITIAL PROCEDURE may also be called explicitly.
The “main” or “top-level” MODULE of a program (the one specified to the MAINSAIL executive as the MODULE to gain control first) must have an INITIAL PROCEDURE, or it returns immediately without performing any action. The execution of the top-level MODULE's INITIAL PROCEDURE leads to execution of the entire program.
A MODULE need not have an INITIAL PROCEDURE. When a MODULE not intended to be used as a top-level MODULE has an INITIAL PROCEDURE, it usually performs initialization of the MODULE's outer and interface variables.
The programmer does not have to give the INITIAL PROCEDURE a name, in which case the compiler supplies the name initialProc. That is:
INITIAL PROCEDURE;
BEGIN ... END
is equivalent to:
INITIAL PROCEDURE initialProc;
BEGIN ... END
The INITIAL qualifier need be specified only in a PROCEDURE
declaration
that includes a PROCEDURE body; i.e., it does not have to be given
in FORWARD or interface field declarations.
However, if it is given in a FORWARD
or a field declaration, it must also be given in the body declaration.
11.12. FINAL PROCEDURE
Each MODULE may contain a single typeless and parameterless
PROCEDURE that is automatically
invoked when the MODULE is disposed.
This PROCEDURE is qualified with FINAL, in accordance with the
syntax described in Section 7.11.
The FINAL PROCEDURE may also be called explicitly.
FINAL PROCEDUREs are often used to dispose of dynamic ARRAYs and data sections that are no longer needed, to release scan bits, or to close files that should not be left open. A FINAL PROCEDURE often “undoes” the work of the INITIAL PROCEDURE.
The programmer does not have to give the FINAL PROCEDURE a name, in which case the compiler supplies the name finalProc. That is:
FINAL PROCEDURE;
BEGIN ... END
is equivalent to
FINAL PROCEDURE finalProc;
BEGIN ... END
The FINAL qualifier need be specified only in a PROCEDURE declaration that includes a PROCEDURE body; i.e., it does not have to be given in FORWARD or interface field declarations. However, if it is given in a FORWARD or a field declaration, it must also be given in the body declaration.
When a MAINSAIL execution terminates, all data sections are disposed, and all open files are closed. The order in which data sections are disposed is unspecified. The programmer must not access a data field of another MODULE in a FINAL PROCEDURE without first ensuring that the other MODULE is bound. The programmer must not perform I/O to a file in a FINAL PROCEDURE without ensuring that the file is open. Bugs that are difficult to track may occur because of dependencies on the order of execution of FINAL PROCEDUREs.
Currently, when a data section is collected by the garbage collector, the MODULE's FINAL PROCEDURE is not executed. This behavior is subject to change.
11.12.1. FINAL PROCEDUREs and $exitingMainsail
MAINSAIL FINAL PROCEDUREs are called when a data section for the
MODULE containing the FINAL PROCEDURE is disposed,
and also when a MAINSAIL execution finishes.
It is sometimes useful in a FINAL PROCEDURE to determine why the FINAL PROCEDURE is being called. FINAL PROCEDUREs that are called because MAINSAIL is exiting do not need to do certain kinds of cleanup, particularly disposal of data structures, since these data structures will go away as soon as MAINSAIL exits anyway. Disposal of a large, complicated data structure can sometimes take a noticeable amount of time and serves no purpose if its only effect is to delay MAINSAIL's exit. On the other hand, FINAL PROCEDUREs called when MAINSAIL is not exiting should clean up any data structures they have allocated, since MAINSAIL execution will continue and the presence of the data structures may impair subsequent performance.
FINAL PROCEDUREs that need to determine whether they are being called because MAINSAIL is exiting can call the BOOLEAN macro $exitingMainsail, which becomes set on entry to the system PROCEDURE exit. If $exitingMainsail returns TRUE, the FINAL PROCEDURE is being called because MAINSAIL is exiting. For example:
FINAL PROCEDURE;
IF NOT $exitingMainsail THEN
disposeMyComplicatedDataStructure;
Except during a call to exit, it is not possible to call a MODULE's interface PROCEDUREs after the MODULE's FINAL PROCEDURE has finished executing, because the MODULE's data section is then immediately deallocated by the runtime system. However, during a call to exit, the FINAL PROCEDUREs of all data sections currently in memory are called in an unspecified order, and the data sections are not deallocated after the FINAL PROCEDURE is called. Thus, interface PROCEDUREs corresponding to data sections that have been deinitialized are still available to be called, even though it may no longer make sense to do so.
Consequently, when $exitingMainsail is set, FINAL PROCEDUREs should be very cautious about making intermodule calls (except to the MAINSAIL runtime system MODULEs, which have been carefully written so that calls to them during a call to exit are not a problem). You should not call an interface PROCEDURE in any MODULE for which the FINAL PROCEDURE has been called, unless you know that that MODULE has been written to handle the situation. Any MODULE that might be called during a call to exit, and which has outer data that should be initialized during a call to any interface PROCEDURE, should include in its interface a flag variable to tell potential callers that its FINAL PROCEDURE has already been called. When $exitingMainsail is set, do not make calls to the MODULE if its flag is set.
In addition, any PROCEDUREs called by the FINAL PROCEDURE while $exitingMainsail is set must also check whether the target of an intermodule call has already had its FINAL PROCEDURE called.
A MODULE CALLEE might set a flag so that a MODULE CALLER can avoid calling it when $exitingMainsail is TRUE as follows:
BEGIN "calleeHdr"
$DIRECTIVE "NOOUTPUT"; SAVEON;
MODULE callee (
BOOLEAN finalProcCalled;
PROCEDURE interfaceProc;
);
END "calleeHdr"
##########################################################
BEGIN "caller"
RESTOREFROM "calleeHdr";
...
FINAL PROCEDURE;
IF NOT $exitingMainsail THENB
... normal cleanup... END
EB # Cleanup that must be done, even during exit
...
IF NOT callee.finalProcCalled THEN
callee.interfaceProc END;
END "caller"
##########################################################
BEGIN "callee"
RESTOREFROM "calleeHdr";
...
FINAL PROCEDURE;
BEGIN
...
finalProcCalled := TRUE;
END;
END "callee"
CLASS c1 (PROCEDURE p1 (INTEGER i));
PROCEDURE p2 (REAL r);
CLASS c3 (PROCEDURE p3 (STRING s));
GENERIC PROCEDURE p "p1,p2,p3";
POINTER(c1) ptr1;
POINTER(c3) ptr3;
the code:
ptr1.p(2);
p(2.0);
ptr3.p("2")
would be equivalent to:
ptr1.p1(2);
p2(2.0);
ptr3.p3("2")
Note that behavior is undefined if a GENERIC PROCEDURE p has a field of a CLASS c as an instance and c also has a field named p; i.e., the behavior of the following is undefined:
CLASS c (PROCEDURE p1; PROCEDURE p);
GENERIC PROCEDURE p "p1";
...
POINTER(c) cp;
cp.p; # undefined
When a control section that was obtained from an individual objmod file is first removed from memory, it is written to a swap file. When a control section obtained from a currently open objmod library is removed from memory, it is not written to the swap file, since it may be read back in from the objmod library.
The name of the swap file on most systems is of the form swpxxx.tmp, where xxx is an integer. This file name format is subject to change and may be different on some systems.
11.14.1. Deletion of Swap Files
If MAINSAIL opens a swap file during execution, it is deleted when
MAINSAIL returns normally back to the operating system. The swap file is
opened with the delete
bit set so that when it is closed, it is deleted.
If MAINSAIL does not exit normally, e.g., if the process is killed
because the user typed a terminal interrupt character,
then the logic that closes all open files is not
executed, and hence the swap file is not deleted. This also happens if
the process exits to the operating system while in foreign code.
11.15. Compilation of Several MODULEs in One File
More than one source MODULE can appear in the same file.
If no
text (other than blank, tab, end-of-page, and end-of-line characters)
appears between the final END of one
MODULE and the BEGIN of the
following MODULE, the MODULEs are compiled just as if they were
separated into different files and each file compiled separately.
Symbols that are defined between the END of one MODULE and the BEGIN of the next (with DEFINE or REDEFINE) normally belong to the preceding MODULE, not the following. The compilation of a new MODULE does not begin until its BEGIN is seen (if something other than BEGIN or a macro definition or compiler directive is seen, it is treated as end-of-file). Compiling a file with the following contents therefore gets an error:
REDEFINE moduleName = "firstM";
BEGIN moduleName
END moduleName
REDEFINE moduleName = "second";
BEGIN moduleName
END moduleName
since the second definition of moduleName belongs to the first MODULE; when the second BEGIN is seen, the compilation of the second MODULE is begun, and the previous definition of moduleName is forgotten. The use of moduleName therefore results in a compiler error message.
Symbols may be defined after the initial BEGIN of a MODULE. The above could be rewritten as:
REDEFINE moduleName = "firstM";
BEGIN moduleName
END moduleName
BEGIN REDEFINE moduleName = "second";
moduleName
END moduleName
This would produce two MODULEs, FIRSTM and SECOND, without error. The $GLOBALREDEFINE directive could also be used in place of REDEFINE in the original example to make it compile correctly.
A MODULE may be a nonbound-invocation MODULE, meaning that
when $invokeModule
(see Section 38.14)
is called it creates a nonbound data section
rather than a bound data section
(if the MODULE's data section is allocated with bind
or by indirect access of an interface PROCEDURE,
a bound data section is created, as usual).
This allows several invocations of a MODULE to coexist,
provided that they are created by $invokeModule.
A MODULE is specified to be nonbound-invocation MODULE
with the compiler subcommand UNBOUND.
This subcommand may be used in a $DIRECTIVE directive; i.e.,
a MODULE containing:
is specified to be a nonbound-invocation MODULE.
In the MAINSAIL compiler, the MODULE COMPIL is a nonbound-invocation
MODULE
(all the other compiler MODULEs are also allocated as nonbound
data sections, created with new).
If, during a compilation, the compiler is
invoked (e.g., with a call to $invokeModule,
or at an Error response: prompt, or from MAINEX or MAINEDIT,
all of which use $invokeModule),
a new instance of the compiler is created that
does not interfere with any other instance.
If a bound data section of
COMPIL were used, then a second invocation of the compiler would
destroy the first,
since both would be using the bound instance of COMPIL.
XIDAK is considering other, more flexible,
approaches to allowing simultaneous invocations of the same program.
11.16. Nonbound-Invocation MODULEs
Temporary feature: subject to change
$DIRECTIVE "UNBOUND";
MAINSAIL Language Manual, Chapter 11