-
Notifications
You must be signed in to change notification settings - Fork 2
Language Specification (2) : Compilation Units and Scopes
Modula-2 programs and libraries consist of one or more compilation units called modules. A program consists of exactly one program MODULE
and any number of library modules. A library module consists of an interface part, called an INTERFACE
MODULE
, and an optional implementation part, called an IMPLEMENTATION
MODULE
. The interface part specifies the client interface of the library. The implementation part implements the library according to the interface. The implementation part is strictly accessible through its interface part only.
Identifiers that are predefined in the language core are pervasive, that is, they are visible in every scope without import. In contrast, predefined identifiers that are provided by special built-in modules are only visible within a scope into which they are imported. Identifiers not predefined are visible within the scope in which they are declared or defined and, if exported, any scope into which they are imported. Additionally, identifiers declared or defined within an interface module are also visible within the corresponding implementation module without import.
Constants and aliases are visible in the scope in which they are defined or declared from the point forward where they are defined or declared. That is, they must be defined or declared before they are referenced.
Types, procedures and variables are visible in the entire scope in which they are defined or declared. That is, they may be referenced before they are defined or declared.
Scope is a measure of the visibility of identifiers. There are three levels of scope. Global scope, module scope and local scope. Module scope is a sub-scope of global scope and local scope is a sub-scope of module scope.
Global scope encompasses an entire program. Only predefined identifiers have global scope.
Module scope encompasses a module. Identifiers that are defined or declared in, or imported into the top level of a module have module scope. Module scopes may not be nested.
Local scope encompasses a PROCEDURE
declaration or a FOR
statement. Identifiers defined or declared within a procedure or the header of a statement have local scope. Local scopes may be nested. The surrounding scope in which a local scope is nested is called the outer scope. The nested scope is called the inner scope.
Identifiers defined or declared in an outer scope are also visible in an inner scope and may be redefined within the inner scope. Redefining an identifier of an outer scope within an inner scope causes the entity denoted by the identifier to be shadowed. A shadowed entity is no longer addressable through its identifier within the scope in which it has been shadowed and within any of its sub-scopes. Identifiers that belong only to an inner scope are visible only in the inner scope and any of its sub-scopes but not in any outer scope.
Procedure scope is created by a PROCEDURE
declaration.
Procedure scope overlaps with the outer scope of the procedure. The outer scope extends to the end of the pro- cedure header and continues after the end of the procedure body. The inner scope extends from the beginning of the procedure header to the end of the procedure body. The procedure header thus belongs to both the outer and the inner scope.
The procedure’s identifier is visible in both the outer and the inner scope. It may be referenced in either scope and may not be redefined in either scope. The identifiers of a procedure’s formal parameters exist in both the outer and the inner scope but are visible only within the inner scope and may not be redefined therein.
FOR
statement scope is created by a FOR
statement.
A FOR
statement scope does not overlap with its outer scope. It extends from the beginning of the FOR
statement’s header to the end of its body. The loop variants declared within the loop header have local scope, they are only visible within the loop.
Identifiers declared or defined within a interface module are automatically exported for import and use by client modules. By contrast, program and implementation modules do not export any identifiers. An IMPORT
directive may be used within any kind of module to explicitly import the identifiers exported by interface modules. An interface module does not implicitly re-export imported identifiers.
IMPORT FooLib, BarLib, BazLib;
A duplicate import is an import of a library that has already been imported into the same scope. Any such import is ignored and will cause a compile time warning.
All imports are qualified. That is, an imported identifier is referenced in the importing module by a qualified identifier. A qualified identifier consists of the module identifier, followed by a period, followed by the actual identifier. Qualification prevents name conflicts between identical identifiers in different libraries.
IMPORT Flintstones, Rubbles;
...
person := Flintstones.Fred;
person.spouse := Flintstones.Wilma;
person.friend := Rubbles.Barney;
person.friend.spouse := Rubbles.Betty;
Imported modules are not implicitly re-exported but they may be explicitly re-exported. Module identifiers to be imported for automatic re-export are marked with a re-export suffix +
.
import := IMPORT libIdent reExport? ( ’,’ libIdent reExport? )* ’;’ ;
alias libIdent = StdIdent ;
alias reExport = ’+’ ;
A library module that imports other modules for the sole purpose of re-export is called an import aggregator. The facility is called import aggregation. It is useful for importing multiple libraries with a single import directive.
INTERFACE MODULE CharIO;
IMPORT SoleCharIO+, ArrayOfCharIO+; END CharIO.
When a client module M
imports an import aggregator which imports and re-exports modules B
and C
, client module M
then automatically imports modules B
and C
.
IMPORT CharIO; VAR char : CHAR; str : ARRAY 10 OF CHAR;
...
WRITE "char: ", char; (* use of syntax bindings in SoleCharIO *)
WRITE "string: ", str; (* use of syntax bindings in ArrayOfCharIO *)
An abstract data type library is a library that provides an abstract data type (ADT) whose identifier matches the identifier of its library module.
INTERFACE MODULE String;
TYPE String = OPAQUE POINTER;
The identifier of such an ADT is implicitly aliased when it is imported and may therefore be used unqualified.
IMPORT String;
VAR s : String; (* alias for String.String *)
The introduction of a name along with an associated specification of an entity is called institution. There are two kinds:
- definition
- declaration
A definition is an institution of interface of an entity, a declaration is an institution of implementation. However, in the case of constants, variables and types other than opaque types, interface and implementation are inseparable. Any institution of a constant, variable or non-opaque type within an interface module is both a declaration and a definition at once.
Copyright © 2015-2018 Modula-2 Software Foundation