Blocks and Procedures
KEY
Block structure and storage
KEY
IMP is a block-structured language. A block is a sequence of statements
of which the first is %begin and the last is %end. The program itself
is regarded as a block, and the first %begin encountered is interpreted
as the start of the program. The statement %end %of %program, rather
than %end, is used to indicate that the end of the program has been
reached. Blocks may be nested to a depth determined by the particular
implementation.
Within each block, variables and constants to be used must be declared
at the head of the block (and before any %on %event statement), unless
they have already been declared at the head of an outer block. Labels
and switch labels are always local to a block; thus it is impossible to
jump from one block to another. Keywords which occur in pairs such as
%cycle ... %repeat must have both elements within the same block.
Example:
%begin
%integer I, J
%real %array A(1:10)
:
%real X, Y
:
:
%begin
%real Z, P
I = 4
J = 10
:
:
%end
:
:
%end %of %program
I and J have been declared at the head of the outermost block and may
thus be referred to from any inner blocks of the program.
If, however, a variable is declared which has the same identifier as a
variable already declared in an outer block of the program, then use of
that identifier will refer to the variable most recently declared.
Example:
%begin
%integer I, J, K
%real X, Y
I = I+1
:
:
%begin
%real I
I = 4.106
:
:
%begin
X = I
:
:
%end
%end
%end %of %program
I is first declared as an integer type and will be allocated storage
accordingly. The first use of I refers to this integer location.
Within the next block, however, I is redeclared as a real variable.
Now space is allocated for a real variable and within this block (and
any deeper blocks of the program - unless I is again redeclared) use
of I refers to the real location and the integer location of the outer
block remains untouched.
When a variable is accessible it is said to be in scope. A variable is
global to a block within the block in which it is declared, if it is
accessible there. In the example above, X, Y, J and K are global to
both inner blocks.
The redeclaration of variables is permissible because of the way in
which storage space is allocated to a program.
Each program can be considered to have space allocated to it on what is
called the stack. The stack is an area of store and the stack space
given to each program has a prescribed layout, as shown in the following
diagram.
stack pointer-----------\
|
|
_____________________________________________________________v________
| Program | Constants, |%own variables, | Cells in | Free \
| Code | %constant variables,|%own arrays | use | cells
| | %constant arrays | | | ...
|__________|______________________|________________|__________|________
\________________________________/ \___________________________________/
| |
read only read and write
The "read only" area contains the object code of the program as produced
by the compiler, and also any of the constant variables declared by the
program. This area cannot be altered during execution of the program.
The "read and write" area has own variables stored first and thereafter
space is allocated according to the requirements of the program. Each
cell will be as big as is required by the entity stored in it: thus an
integer variable occupies only a small cell, whilst a complex record
variable may require a very large cell.
The stack pointer holds the address of the next free location in store.
Information stored on the stack is kept as compact as possible though
with due regard for alignment considerations. This is of particular
significance on paging machines, as it allows the program to run with a
minimum number of page faults.
The following example illustrates the stack mechanism.
Example:
%begin
%real A,B,C
%integer I,MAX
%real %array X(1:3), Y(1:4)
As a result of the above declaration, the stack might look like this:
ST1 ST2---\
| |
v________________________________________________________________v_
| | | | | | | | | | | | | | |
| A| B| C| I| MAX|###|X(1)| X(2)| X(3)|###|Y(1)| Y(2)| Y(3)| Y(4)| ..
|__|__|__|__|____|___|____|_____|_____|___|____|_____|_____|_____|___
ST1 is the position of the stack pointer before the %begin, and ST2 is
its position after the declarations. The shaded areas indicate
portions of store which contain information essential to the program
(such as array dimensions) but which cannot be accessed by the
program.
The stack pointer may be advanced by any further declarations or by
activity initiated by the instructions of the program.
On entry to a new block or procedure, the stack pointer is advanced as
necessary to cope with new declarations, etc. On exit from the block
(or procedure) it returns to its last position prior to entry to that
block (or procedure).
Storage space for fixed variables such as %real or %integer types is
determined at compile time, but arrays with dynamic bounds cannot be
allocated space until the values of the bounds are determined at run
time.
Since the block structure of a program is so closely related to the
allocation of store, skilful use of blocks can lead to economical use
of store. Consider the following examples.
Example:
%begin
%integer N
%cycle
READ(N)
%exit %if N=0
%begin
%integer I
%integer %array A(1:N)
READ(A(I)) %for I=1,1,N
:
:
%end
%repeat
%end %of %program
The required size of the array is read in the outer block and the
array itself declared in the inner block. Thus the space used by any
one set of data will be recovered when the inner block is left, so
allowing one to repeat the process without incurring successively
increasing demands for storage space.
Example:
%begin
:
:
%begin
%real %array XYZ(1:5000)
:
:
%end
:
:
%begin
%integer %array IJK(1:20, 1:250)
:
:
%end
:
:
%end
Since the declarations at the head of a block are cancelled on executing
the %end of the block, it is often possible to economise on storage
space if a program consists of several distinct tasks, each requiring
large amounts of space. The above example illustrates the point.
Procedures can be used in a similar way to economise on store.
Events
KEY
During the execution of a program, events may occur which normally
cause the program to terminate with an error message. However, there
is a mechanism which allows events to be intercepted and used to
control the subsequent execution of the program. This mechanism is
activated by the use of the %on %event statement.
The %on %event statement (which may occur only once in any block) is
used to introduce a block of statements which is only executed if one
of the specified events occurs. The form of the %on %event block is:
%on %event <nlist> %start
<executable instructions>
%finish
<nlist> is a list of integers in the range 1 to 14 inclusive, where
each number refers to a specified class of error as follows:
1 Overflow
2 Excess resource
3 Data error
4 Invalid data
5 Invalid arguments
6 Out of range
7 Resolution failure
8 Undefined value
9 I/O error
10 Library procedure error (e.g. SQRT negative)
11-14 Available for user definition
{EMAS IMP80: event 11 is used by the ERCC Graphics Package.}
{IMP77: there are two extensions to the range of event numbers:
Event 0 is defined - Termination
Event 15 is available for user definition}
{IMP77: the form
%on %event * %start
is permitted. It is a shorthand way of specifying every event number;
i.e. 0,1,...,14,15.}
Up to 255 sub-events may be defined for each event, but these cannot
be specifically intercepted and are necessarily
implementation-dependent. For example, not all machines distinguish
integer overflow from real overflow. Sub-events defined for some
implementations are listed in Appendix B.
The %on %event block must follow the declarations at the head of a
block and may be regarded as the last declaration of the block. The
code within the %start ... %finish is not executed by entry through
the head of the block, but is jumped to on the occurrence, during the
execution of the block, of an event referenced by the event list.
Following such a jump, the flow of control is determined by the
contents of the %on %event block; the program does not resume at the
point of the failure.
An event may be forced to occur by the instruction
%signal %event <const>, <exprn>
where <const> specifies the event required and <exprn> is an optional
integer expression (evaluating to an integer within the range 0-255)
which may be used to specify sub-event information.
The use of the %signal %event statement is the only way of causing a
user-defined event (i.e. one which is not pre-defined by the
implementation) to occur, although it can also be used with the
predefined events (1-10).
If an event is forced by a %signal %event statement in an %on %event
block which includes the occurring event in its event list, a branch
is not made to the head of that block, since such a branch would
probably cause looping. Instead, the event is traced up the stack
through each superior block until either an %on %event statement
including the occurring event in its list is found, or the user
environment is left. If a suitable %on %event statement is found,
control is transferred to its %start ... %finish block.
In parallel with these language statements, two standard integer
functions are provided which enable the programmer to determine
further information when an event occurs. They may only be
meaningfully called in a block which has an %on %event statement
within it.
%integer %function EVENT INF
This function returns
(event no<<8) ! sub-event no
for the last event which has occurred. An error occurs at
compile time if the function is called in a block with no %on
%event statement, and an undefined value will result at run time
if no event has in fact occurred when the function is called.
%integer %function EVENT LINE
This function returns the program line number at which the last
event occurred during execution of the block in question
(provided the program was compiled with line number updating;
otherwise 0 will be returned). If no event has occurred, an
undefined value will result.
If an event is not intercepted in the block in which it occurs,
then it is traced up the stack through each superior block until
either a suitable %on %event statement is encountered or the
user environment is left, the diagnostic package being entered
in the latter case. When a suitable %on %event statement is
encountered in an outer block, program control is transferred to
its %start ... %finish block.
As a result of these facilities it follows that, for example "input
ended" may be detected and dealt with from within an external routine
or a routine within a main program.
Examples:
%on %event 10 %start
%if EVENT INF&255#8 %start
%signal %event 10, EVENT INF&255
! All subevents except 8 are passed to outer block
%finish
:
: ! Code to deal with subevent 8 of event 10
:
%finish
System defined events
%integer SUBCLASS, EVENTNO
%constant %string(21)%array MESSAGE(1:2) = %c
"Capacity exceeded",
"Array bounds exceeded"
%on %event 6 %start
SUBCLASS = EVENT INF&X'FF'
EVENTNO = EVENT INF>>8 & 15
%if 1<SUBCLASS<=2 %then PRINTSTRING(MESSAGE(SUBCLASS)) %c
%else PRINTSTRING("Invalid subclass")
NEWLINE
->ERROR EXIT
%finish
:
:
ERROR EXIT: ....
User defined events
%integer SUBEVENT
%on %event 12 %start
PRINTSTRING("Event 12 has been intercepted")
NEWLINE
->EVENT 12
%finish
:
:
SUBEVENT = 2
:
:
%signal %event 12, SUBEVENT
:
:
EVENT 12: .....
{IMP77: a standard record map is provided to enable the programmer to
determine further information when an event occurs.
%record %format EVENT FM(%integer EVENT, SUB, EXTRA)
%record(EVENT FM)%map EVENT
EVENT returns a reference to a system-provided record which
contains the parameters of the last event to have been
signalled. If no event has been signalled all the fields of the
record are set to zero.
Consult Appendix B and documents relating to specific
implementations of IMP77 for details of the information
returned.}
Note that the event mechanism is designed for intercepting the
occasional event only - overheads are high if the %on %event statement
is used frequently.
Procedures
KEY
A procedure takes the form of a block in which the first %begin is
replaced by the procedure heading. However, a procedure can only be
entered by execution of a procedure call statement, whereas a block is
entered when the %begin statement at the start of the block is executed.
There are three forms of procedure - %routine, %function (or %fn) and
%map.
Example:
%begin
%integer %array A(1:40)
:
:
%routine CLEARA
%integer I
A(I) = 0 %for I=1,1,40
%end {Of %routine CLEARA.}
:
:
CLEARA
:
:
%end %of %program
In this example, a procedure - routine CLEARA - is described and then
used by means of the procedure call statement 'CLEARA'.
CLEARA is effectively a named block: if the procedure heading were
replaced by %begin and the procedure description moved down to take the
place of the procedure call statement, the effect would be exactly the
same. However, by making the block a routine - and thus giving it a
name - it is possible to call it at different places in the program
without repeating the description each time.
In addition, procedures can have parameters, passed to them via a
parameter list enclosed in brackets in the procedure call statement.
Example:
%begin
%integer F, T
%string(31) %array A(0:99)
%routine STRINGSORT(%string(*)%array %name X,
%integer FROM, TO)
%integer L, U
%string(255) D
:
:
%end {Of %routine STRINGSORT.}
:
:
STRINGSORT(A, F, T)
:
:
%end %of %program
In the above example, the procedure heading specifies the form of
procedure (%routine) being described, gives the procedure the
identifier STRINGSORT and describes the number, order and types of
variables passed as parameters, using dummy names. The procedure is
entered when the call statement STRINGSORT(A, F, T) is executed.
The first line of a procedure has one of the following forms:
%routine <name> (<parameter list>)
<type> %function <name> (<parameter list>)
<type> %map <name> (<parameter list>)
where <type> is one of the arithmetic types, or %string(<n>) or
%record(<format>), and (<parameter list>) is optional.
{EMAS IMP80: record functions and record maps are not available.}
A procedure must be declared before it is called. If the procedure
itself is placed at the head of a block (as above) no further
declaration is needed. Otherwise a specification (%spec) statement must
be placed amongst the other declarations at the head of a block, with
the procedure included later at the same textual level. The %spec
statement is exactly like the procedure heading with %spec inserted
after the keyword %routine, %function or %map.
Example:
%routine %spec STRINGSORT(%string(*)%array %name X,
%integer FROM, TO)
It is most important to give the specification accurately: in
particular, the number and types of the parameters must be correct.
However, the names of the parameters in a specification statement are
not significant.
Variables used in procedures may be declared locally (as is the case for
L, U and D in the above example), but any information stored in them
becomes inaccessible on exit from the routine. If the information
calculated by the procedure is to be preserved for use on subsequent
entries, it must be stored in global variables or %own variables
declared locally.
Global variables should be used with care in procedures. Note that such
variables must be declared globally to the procedure itself: it is not
sufficient that they be declared globally to the procedure call.
Example:
%begin
%integer X, Y
%routine CONVERT
X = ....
Y = ....
%end
CONVERT
:
:
%begin
%integer X
:
:
CONVERT
:
%end
:
:
%end %of %program
The routine CONVERT, which has no parameters, operates on variables X
and Y. The first call of the routine uses X and Y as declared at the
head of the program. The second call occurs from within an inner
block in which X has been redefined. However, the procedure again
uses the X declared at the head of the program, ignoring the redefined
X of the inner block.
Information calculated by a procedure and stored in global variables is,
of course, accessible on exit from the procedure.
There are three categories of procedure which may be called by a
program:
1) standard procedures, which are automatically available to all
programs (see Section 7); for example, READ, INT PT;
2) procedures described within the program;
3) external procedures, which are compiled separately from the program
(see Section 3.3).
Procedures can be nested: that is, a procedure can be defined inside
another procedure. The scope rules apply as before.
Procedures can be used recursively: that is, a procedure can be called
from within itself. The example in 3.2.1 of a sorting program
demonstrated the recursive use of routine STRINGSORT. Obviously, some
criterion within the body of the procedure must eventually prevent the
procedure calling itself endlessly. In the example, the recursive calls
of STRINGSORT are embedded in conditional instructions, thus providing
the necessary opportunity to stop the recursion process.
Parameters
KEY
When a procedure with parameters is described, the heading statement
contains dummy names for the variables passed as parameters. These
names are used in the description of the procedure and are referred to
as "formal" names. When the procedure is executed, however, "actual"
parameters take the place of the formal parameters, and are specified
in the parameter list of the particular call. The parameters given in
the procedure call must correspond in number, order and type with
those specified by the procedure heading. Local storage is allocated
within the procedure for each formal parameter.
Parameters fall roughly into two categories - those passed "by value"
and those passed "by name". When a procedure call is executed,
parameters passed by value have their values assigned to the
corresponding local variables. An arithmetic expression may thus be
passed as a parameter: it will be evaluated before entry to the
procedure and its value assigned to the appropriate formal parameter.
On exit, it will become inaccessible and any information stored in it
will be lost.
Example:
%begin
%real Z, Y
%real %function AUX(%real X)
:
:
%end
:
:
Y = AUX(Z)
:
:
Y = AUX(4.5*Z)
:
:
%end %of %program
Function AUX has formal parameter X of type %real. Execution of
the first call of the function will assign to X the value to be
found in Z. On executing the second call, the expression 4.5*Z
will be evaluated and the result assigned to formal parameter X.
Parameters passed "by name", however, are treated differently. The
local storage allocated for a parameter passed by name is a reference
variable of the appropriate type. In this case, when a procedure call
is executed, the effect is that the local variable is "pointed at" the
actual parameter, which must therefore be the name of a variable, and
not an expression. Thus, every reference in the procedure to the
formal parameter is treated as if it were a reference to the actual
parameter. Parameters passed by name can be used to preserve
information calculated by the procedure, for use on exit.
Example:
Given a procedure with heading
%routine ALPHA(%integer BETA, %real %name GAMMA)
the call
ALPHA(J*K+4, R(M))
has the effect of causing the following assignments to be carried
out on entry to the procedure:
BETA = J*K + 4
GAMMA == R(M)
(Note however that these two assignments as given may violate the
normal rules for scope of names, because they use variables in
scope at the point of the call to assign to variables local to the
routine body.)
Example:
%begin
%constant %string (1) SNL = "
"
%string(31)%array NAMES(1:99)
%routine %spec STRINGSORT(%string(*)%array %name X,
%integer F, T)
%integer I, N
READ(N) %until 1 <= N <= 99; ! Reject if not in range.
READSTRING(NAMES(I)) %for I=1,1,N
STRINGSORT(NAMES,1,N)
PRINTSTRING(NAMES(I).SNL) %for I=1,1,N
%routine STRINGSORT(%string(*)%array %name X,
%integer FROM, TO)
%integer L, U
%string(255) D
%return %if FROM >= TO
L = FROM; U = TO
D = X(U)
%cycle
L = L+1 %while L < U %and X(L) <= D
%exit %if L = U
X(U) = X(L)
U = U-1 %while U > L %and X(U) >= D
%exit %if U = L
X(L) = X(U)
%repeat
! Now L = U.
X(U) = D
L = L-1; U = U+1
STRINGSORT(X,FROM,L) %if FROM < L
STRINGSORT(X,U,TO) %if U < TO
%end
%end %of %program
This program sorts a set of strings held in array NAMES. Note the
%routine %spec statement at the head of the block, with the routine
description occurring later at the same textual level.
There are three parameters passed to the %routine STRINGSORT; the
two integers are passed by value, and the string array is passed by
name. Arrays can only be passed by name, to prevent unnecessary
allocation of storage space and time-consuming copying. When the
routine is called the first time the parameters are treated as
follows. The string array NAMES is passed by name and thus all
references to the formal parameter X within the body of the routine
become references to NAMES; the actual value 1 will be assigned to
formal parameter F; and the value stored in N will be assigned to
formal parameter T. On exit from the routine, NAMES will have its
elements sorted, but the value of N will be unchanged.
Strings may be passed as parameters to procedures. Where a string
name parameter is used, the length specified in the procedure heading
can be replaced by * so that strings of any length (up to the allowed
maximum) can be passed to that procedure. In this situation run-time
overflow checking is applied to the actual string passed to the
procedure.
In IMP, parameters called by name are assigned at the time of call.
Thus if a routine with parameter list (%realname X, %integername I,
...) were called with parameters (A(J), J, ...) where A is the name of
a previously declared real array, then on execution of the procedure
every reference to X will refer to the element of A determined by the
value of J on entry, no matter how J varies during execution of the
procedure.
Procedures, too, may be passed as parameters:
Example:
%begin
%routine ONE(%routine PARAM(%integer X) )
:
:
%end
%routine TWO(%integer P)
:
:
%end
%routine THREE(%real X)
:
:
%end
ONE(TWO)
%end
Routine ONE has a single parameter, a routine with a single
parameter of type %integer.
Note that routine THREE cannot be passed as parameter to routine
ONE because THREE has a parameter of type %real. That is, the
parameter list of the actual procedure passed must correspond with
the parameter list of the corresponding formal procedure parameter.
To summarise: a formal parameter can be any of the following types:
1) any arithmetic type (e.g. %long %real, %short %integer)
2) %string(n)
3) %record(format)
4) any of the above followed by %array %name
5) any type of procedure (i.e %routine, %function of any type, %map of
any type)
Items (1) - (3) correspond to call by value; items (4) and (5) to call
by name. The actual parameter in a call by value must be an expression
of the appropriate type; in a call by name it must be a "reference" to
an entity of the appropriate type.
Routines
KEY
A routine call may be used exactly like an instruction. When the call
is executed, control is transferred to the routine, which executes
until either the %end statement is reached or a %return statement is
encountered. Flow of control is resumed at the statement after the
routine call.
Example:
:
:
%integer X,Y
%routine CONVERT
%if X < Y %start
X = X+Y
%finish %else %start
X = X-Y
%finish
%end
:
:
CONVERT
:
:
CONVERT %unless X = 0
:
:
Note that CONVERT uses global variables X and Y and that the result
is stored in X on exit from the routine. Note also the use of
CONVERT in a conditional statement.
Functions
KEY
A function calculates a value of the specified type (integer, real,
string or record), and may be used in an expression exactly like an
operand of that type. The function terminates when an instruction of
the form
%result = <expression>
is executed, and the value of the expression, which must be of the
same type as that of the function, is returned to the statement making
the call.
{EMAS IMP80: record functions are not available.}
Example:
%integer %function SUMSQ(%integer A, B)
%result = A**2 + B**2
%end
%integer X, Y, Z
:
:
Z = SUMSQ(X,Y) - 3
:
The use of global variables in functions may have unusual side
effects. Such side effects can be difficult to detect and should be
avoided.
Example:
%begin
%integer I, J, K
%integer %function SIDE
I = I+1
%result = J
%end
:
K = I+SIDE
:
%end
In the statement K = I+SIDE, there is no defined order of
evaluation of operands in the expression on the right hand side of
the assignment, but the value of I used in the expression depends
upon the order of evaluation. Thus the value of the expression is
indeterminate. The actual value computed depends on the
implementation of IMP80 used.
Functions may be of %string type; in this case the maximum length of
the string which may be returned by the function is included in the
specification and heading of the function.
Example:
%string(20)%function FIELD(%integer I)
Functions may be of %record type; in this case the format of the
record returned must be included in the specification and heading of
the function.
Example:
%record %format RFA(%integer ONE, TWO, THREE)
%record (RFA) %function TRIPLES(%integer ITEM)
:
{EMAS IMP80: record functions are not available.}
Maps
KEY
A map (or "mapping function") calculates a reference to a variable of
the specified type (integer, real, string or record) and may be used
exactly like a variable of that type. The map terminates when an
instruction of the form
%result == <ref to a variable of the same type as the map>
is executed and the address of the given variable is returned to the
calling instruction. The "reference to a variable" on the right hand
side of the %result statement can be a normal variable, a reference
variable or a mapping function call.
{EMAS IMP80: programmer-written record maps are not available.}
{EMAS IMP80: a %result statement in a map can be of the form
%result = integer expression
where the integer expression gives the address of some location. It
usually includes a call of the function ADDR, described in Section 6.}
{IMP77: the keyword %map may be replaced by %name %function or %name
%fn.}
Example:
%integer X, Y, K
%integer %map MIN
%if X < Y %then %result == X %else %result == Y
%end
MIN = 0
! This statement is exactly equivalent to
! %if X < Y %then X = 0 %else Y = 0
:
K = MIN
:
Note the use of a map on the left-hand side or right-hand side of
an assignment statement.
Where a map is of %string type, the specification and heading must
include a length - the precise maximum length of any string to which
the map may refer.
Example:
%string(3)%map XA(%integer I)
If a string map might refer to strings of varying length, then the
procedure heading and declaration may have the symbol '*' in place of
a specific length. The map may then reference strings of any length
up to the allowed maximum.
Example:
%string(*)%map XA(%integer I)
:
Where a map is of %record type, the specification and heading must
include a record format. The right hand side of a %result statement
within a record map must refer to a record of the same type as the
map, or to the standard record map RECORD (described in Section 6).
Example:
%record %format RF(%integer X, %string(10) TITLE)
%record (RF) %map RM(%integer I, J)
%record (RF) %name CURRENT
:
CURRENT == ...
:
:
%result == CURRENT %if ...
:
%end {Of %record %map RM
%string(15) HEAD
%integer P, Q
:
HEAD = RM(P,Q+1)_TITLE
:
RM(17,3)_X = 24.6
:
{EMAS IMP80: programmer-written record maps are not available.
However, a standard record map RECORD is provided; it is described in
Section 6.}
{IMP77: the record format given in the specification or heading can be
replaced by (*), meaning that the reference returned by the map may be
to a record of any format; the actual record format used depends on
the context.
Example:
%record(*)%map SURVEY(%integer I, %real X)
The standard map RECORD (described in Section 6) is of this sort.}
A number of standard maps are provided. These are described in
Section 6.
External linkage
KEY
A complete program may be divided into separately compiled modules which
are linked before (or possibly while) the program is executed. This
section describes the language facilities provided for setting up or
accessing a separately compiled module.
A procedure compiled separately from a program which uses it is called
an external procedure. If a program uses an external procedure it must
contain an external specification statement. This is of the same form
as the %spec statement described in Section 3.2, but with the prefix
%external.
Example:
%external %string(*) %function %spec I TO S(%integer A)
An %external...%spec statement has the same effect as a normal %spec,
except that there is no description of the procedure later in the
program. %external ... %spec statements may be given wherever other
%spec statements would be valid.
The keywords %system and %dynamic may be used in place of %external;
refer to the relevant system documentation for details of the effects of
these keywords.
External variables are also available. If a program uses an external
variable it must include an external specification. This is of the same
form as the variable declaration statement but with the keywords
%external and %spec added.
Examples:
%external %integer %spec WAIT, CHOICE
%external %real %array %spec MEAN(-6:6)
External files
KEY
A file of external procedures and variables may be compiled. Such a
file differs from the structure of a program file (described in
Section 1.2) in several respects:
* There is no initial %begin.
* %end %of %program is replaced by %end %of %file.
* Variables declared outside any procedures must be %own, %constant
or %external (described below).
* The first statement of any procedure description can be preceded by
the keyword %external; such a procedure can then be made accessible
to other programs, as explained above. If a procedure in the file
is not %external then it is accessible only within the file, in
accordance with the normal scope rules.
* %begin/%end blocks are not allowed, except within procedures.
{IMP77: a single %begin/%end block at the outer level is allowed,
so long as it immediately precedes the %end %of %file statement.
The block's %end statement and the %end %of %file statement can
then be replaced by the single statement %end %of %program.}
* Where an %external ... %spec statement in the file specifies an
external procedure or variable described later in the same file,
the keyword %external may be omitted.
An %external variable has all the properties of an %own variable, but
is declared with the keyword %own replaced by %external.
Examples:
%external %integer CHOICE = 6, WAIT = -5
%external %real %array MEAN(-6:6) = 2.7(5),0.3,1.5(*)
External variables can be declared in an external file or in a normal
program file, wherever other declarations are valid. They are
normally declared in the outer block of an external file.
Note that external variables may be initialised, like %own variables,
when they are declared, but not when they are specified in an
%external ... %spec statement.
Example of an external file
KEY
%external %integer IN=0, OUT=0
%routine GET(%integername SYM)
READSYMBOL(SYM)
IN = IN+1
%end
%routine PUT(%integer SYM)
PRINTSYMBOL(SYM)
OUT = OUT+1
%end
%external %routine PROCESS
%integer CH
:
GET(CH) %until CH='*'
:
PUT(CH)
:
%end {Of PROCESS}
%end %of %file
A program making use of the external file:
%begin
%external %routine %spec PROCESS
%external %integer %spec IN, OUT
%integer DATA, DMAX
:
%for DATA = 1,1,DMAX %cycle
IN = 0; OUT = 0
PROCESS
PRINTSTRING("Calculation no.")
WRITE(IN,1)
PRINTSTRING(" characters in;")
WRITE(OUT,1)
PRINTSTRING(" characters out.")
NEWLINE
%repeat
:
:
%end %of %program
%alias
KEY
Any identifier given in an %external declaration or specification may
be followed by %alias string constant, where the string constant
specifies the string to be used for external linkage. This has no
effect on the use of the identifier within the program.
Examples:
%external %long %real %fn %spec EIGENVALUE %alias %c
"D#REFEIGEN$" (%long %real %array %name MAT)
%external %integer RESULT %alias "ICL9CERETURN" = 4