You are on page 1of 13

INFOR: Information Systems and Operational Research

ISSN: 0315-5986 (Print) 1916-0615 (Online) Journal homepage: https://www.tandfonline.com/loi/tinf20

A Practical Examination Of Prefixed Procedures

J.R. Parker

To cite this article: J.R. Parker (1984) A Practical Examination Of Prefixed Procedures,
INFOR: Information Systems and Operational Research, 22:3, 249-260, DOI:
10.1080/03155986.1984.11731926

To link to this article: https://doi.org/10.1080/03155986.1984.11731926

Published online: 25 May 2016.

Submit your article to this journal

View related articles

Full Terms & Conditions of access and use can be found at


https://www.tandfonline.com/action/journalInformation?journalCode=tinf20
A PRACTICAL EXAMINATION OF PREFIXED PROCEDURES

J.R. PARKER
Department of Computer Science, University of Calgary, Calgary, Alberta

ABSTRACT
Procedure prefixing involves the extension of an already existing procedure, using a set of
rules similar to those for class prefixing in SIMULA. Here, issues involving the implementa-
tion of prefixed procedures are considered, and a set of consistent rules are applied to
prefixed procedure declarations.

RESUMi
Le proc^d^ de prefixation implique la prolongation d'une procedure deja existante, utilisant
un ensemble de regies semblables a celles de la classe de prefixation du SIMULA. Ici, les
issues embrassant I'augmentation des procedures de prefixes sont considerees, et un
ensemble de regies consistantes est applique aux declarations du procede de prefixes.

The prefixed procedure concept was introduced by Jean Vaucher^^' as a


tool for structuring operations. It is a simplification of the more general
class prefixing technique found in SIMULA'^' and could be applied to any
language that uses procedures. Briefly, a prefixed procedure involves the
specification of a procedure using the specification of an already existing
procedure. The new (prefixed) procedure defines code, local declara-
tions, and arguments that are concatenated in a special way to the code
and definitions provided by the old (prefixing) procedure. If the
procedures involved implement operations, then this technique allows a
hierarchy of operations to be established.
From a practical point of view, a precise set of definitions is required
before an implementation can be attempted. In my efforts to incorporate
procedure prefixing into programming languages, a reasonable collec-
tion of definitions were decided on, and a few alternatives for imple-
mentations were tried. In particular, a new language named SAHARA*^^ was
developed that includes procedure prefixing, and a pre-processor for
PASCAL was also attempted.

*Received November 1982; revised December 1983


249
INFOR vol. 22, no. 3, August 1984
250 J.R. PARKER

A REVIEW OE THE METHOD

The method is best illustrated by using examples. Consider the PASCAL


procedure named SET as follows:
procedure SET (var x: itemset);
var i: item;
begin
for i: = firstitem to lastitem do
if i in X then
inner;
x: = []
end;
The purpose is to perform some as yet undefined operation, denoted by
the token inner, on all members of a particular set. The set is then made
empty. Note that the inner construct is not a procedure call but is a
statement that marks the place where code belonging to future prefixed
procedures will be placed. The inner can be thought of as some arbitrary
set of operations that will be specified later. Now consider a procedure
PRINT that is prefixed with the name SET as follows:
SET procedure PRINT;
begin
write ii: 6)
end;
The procedure PRINT may be invoked using the usual PASCAL procedure
call syntax:
PRINT (x):
The executable statements for PRINT are placed where the inner
statement appears in SET, the argument lists and local declarations of the
two procedures are concatenated, and a "new" procedure results that has,
in a sense, the properties of both procedures. The code ior PRINT that is
equivalent to the prefixed procedure given would be:
procedure PRINT (var x: itemset);
var i: item;
begin
for i: = firstitem to lastitem do
if f in X then
write (i: 6);
x: = [];
end;
PREFIXED PROCEDURES 251

The resulting procedure prints the values of all members of the set
argument X. The procedure SET defines a class of procedures that have
the property that the first argument represents a set of items, the elements
of which are to be operated upon. The procedure SET continues to exist
and is usable as a procedure by itself. This example, while simple, does not
completely illustrate the usefulness of the prefixed procedure concept.
A more relevant example, in that it better represents the intent and
generality of the procedure prefixing, involves updating variables in a
critical region. Operations on data shared by several concurrent processes
require that some form of mutual exclusion be enforced. If the existence
of procedures P and V is assumed, where P and V perform Dijkstra's
semaphore operations'^', the procedure ENTRY can be written:

procedure ENTRY;
begin
P(semaphorel);
inner;
y(semaphorel);
end;
This again defines a class of procedures prefixed by ENTRY that will
enforce mutual exclusion on the critical region protected by semaphore 1.
For example:
ENTRY procedure UPDATE{val. real);
begin
Protected-Variable: = val;
end;
Execution of the procedure UPDATE involves a P operation before and a
V operation after, the update operation is performed, thus guaranteeing
mutual exclusion.
The most important effect of prefixed procedures is to bracket the
components of an operation that is implemented as a procedure. This
leads to better structure and more reliability, since correct initialization
and termination criteria can be promised. Prefixed procedures also
eliminate duplication of code when many procedures have similar
specifications, and they can sometimes be used to avoid procedure calls.
This is purely an efficiency consideration.
Superficially, procedure prefixing seems to resemble the use of
procedure valued parameters. The two are not the same, as is illustrated
by the following simple example. Procedure SET and procedure PRINT
could be written using procedure valued parameters such as:
252 J.R. PARKER
procedure S£r(var x: itemset; procedure DO/r(i: integer));
var i: item;
begin
for i: = firstitem to lastitem do procedure PRINT;
if i in X then begin
DOITii); write (?: 6);
x: = [ ] end;
end;

SETii, PRINT); {Tbe call to SET}


The meaning of the above code is the same as that of the original version
that uses prefixing, but tbe generality is lost. The procedure valued
parameter DOIT is constrained by its arguments to operate on only one
thing - an integer. There is no feeling that the procedure SET has been
extended, but instead that it is restricted by the constraints on the formal
parameter DOIT. Neither the structuring facility nor the generality of
procedure prefixing is seen in the use of procedure valued parameters.
Moreover, the passing of a procedure valued parameter is done dynamic-
ally at run time, as opposed to statically at compile time, as would be done
for a prefixing scheme.

A SYNTAX FOR PASCAL

The addition of prefixed procedures to PASCAL involves a few small


changes to the syntax. The first is the addition of inner as a reserved word
and as a statement. The semantics are described in tbe previous and next
sections.
The second addition is the reserved word prefix, that must precede the
specification of a prefix name for a procedure. For example, the
procedure PRINT, above, would be rewritten using tbis syntax as:
prefix SET procedure PRINT;
The purpose of prefix is to allow one symbol look-ahead during a parse of
the new syntax.

RULES AND SEMANTICS FOR PREEIXING

As for any structure in a programming language, a reasonable set of rules


for the use of prefixing must be established. This will be done using the
usual rules for procedural languages (e.g. scoping) as a basis.
PREEIXED PROCEDURES 253

General rules
1. Any user-defined procedure may be used as a prefix and such use has
no effect on the prefixing procedure.
2. A prefixed procedure is concatenated with the prefixing procedure in
the following manner. Consider the definitions:

procedure A(argsA); prefixA procedureiJ(argsiJ);


locsA; locsB;
begin begin
code 1 A; codeljB;
inner; inner;
code2A; code2jB;
end; end;

where 'argsA' and 'argsB' are arbitrary argument lists, locsA' and 'locsZJ'
are arbitrary local definitions, and "code..." represents arbitrary sections
of code. In this general case, procedure B resolves itself into

procedure B(argsA; argsi?);


locsA;
locsB;
begin
codelA;
codeIB;
inner;
code25;
code2i4;
end;

3. A prefixed procedure may in turn be used as a prefix for any number


or procedures. Here the prefixing procedure is itself prefixed, and is
resolved before being used to define another procedure. For example,
using the definitions of A and B above, we have:

prefix B procedure C(argsC);


locsC;
begin
codelC;
inner;
code2C;
end;
254 J.R. PARKER

The prefixed procedure C is now equivalent to:


procedure C(ArgsA; Argsfi; ArgsC);
locsA; locsfi; locsC;
begin
code1A;codeIB;codelC;
inner;
code2C; code2B; code2A;
end;
4. The inner statement has the effect of a no-operation or a continue
statement when encountered in a procedure that is unprefixed or in
the main program. If no inner statement appears in a procedure, one is
assumed to exist immediately preceding the terminal end statement of
the procedure block.
5. Only one inner statement is permitted in a procedure. The reasons for
this are aesthetic-use of multiple inner statements introduces an
unneccessary complication, and does not assist in accomplishing the
stated goals of prefixing. However, an implementation could provide
for multiple inner statements without too much difficulty.
6. An identifier being used as a prefix does not have to be defined before
it is used, as happens with identifiers in PASCAL. The reasons for this
are that: first, these general rules are not to be specific to any particular
language, and, second, that it may be difficult to textually arrange a
program so that a procedure is both defined before it is called, and
before it is used as a prefix. A prefix must, however, be defined at some
point in the program.

Recursive prefixing
A prefix chain is a list of prefix names applied to a particular procedure in
order of precedence. The prefix chain for an unprefixed procedure is
empty. For the procedure C above, the prefix chain is "A B." The prefix
level of a given procedure is the number of prefixes to which the
procedure is subject; for example, the prefix level of an un-prefixed
procedure named X is zero, and that of a procedure prefixed with only X
would be one. The prefix level can also be defined as the length of the
prefix chain.
A prefix chain is said to be recursive when it contains more than one
occurrence of the same procedure. The simplest example of recursive
prefixing is the case where a procedure is prefixed with its own name. For
example:
prefix P1 procedure PI;
PREFIXED PROCEDURES 255
This procedure definition suffers from the same problem as the PASCAL
record definition:
type REC = record
Al: integer;
R: REC;
end;
That is, the recursive definition is never properly terminated. Hence, in
both cases, an infinite amount of storage would be required to satisfy the
definition, and so both are illegal.
For the record definition REC, the field R can be replaced by a pointer
field. This allows the recursion to be controlled by the programmer, who
explicitly allocates records as needed. Similarly, the procedure Fl is
probably meant to be recursive, and not prefixed, where the programmer
could control the depth of recursion. The use of pointers and explicit
storage control allows the use of self referencing structures in most
languages. The corresponding technique for procedure prefixing seems
to be to make the recursion explicit in the calls, instead of in the definition.
Recursive prefixing should never be permitted.

The scope of prefix identifiers


The problenoL of scope is one of determining when a prefix identifier can
be "seen," and therefore be used as a prefix. The rules for prefixes need
not be the same as for other identifiers, but it turns out that they are
closely related. What makes prefix identifiers uncommon is that they
carry no type with them. Instead, a prefix represents a structure imposed
on code, and this structure depends on the static environment of the
procedure being used as a prefix. It is for these reasons that the scope
rules for prefixes must be re-examined.
Before the scoping problem can be addressed, the problem of static
environment resolution must be solved. If two procedures Pn and Pm, are
declared in different static environments, and Pn is a prefix of Pm, then
what will be the environment of Pm} The answer that makes the most
sense is that Pm has the environment of both Pn and Pm, depending on
exactly which code is being executed at the time. While the code for Pn is
being executed, the environment is that of procedure Pn, and similarly
for Pm. This rule is simply extended in the logical way for longer prefix
chains.
This rule for resolving static environments affects the rules for prefix
scopes. The rule is as follows: if a prefix identifier is not defined in an
enclosing block of the prefixed procedure, then it may not be used as a
prefix. The reason is obvious if we consider the case in figjure 1.
256 J.R. PARKER

program MAIN... ;

procedure J4 ... ;

prefix? procedureX ... ;

procedure B;
endB;
end X;

prefix X procedure C;
end C;

procedure D;
endD;
end A;
procedure £ ... ;
procedure F;
endF;

endE;
end MAIN.
FIG. 1. A scoping example

Note that the prefix F is not defined in a block enclosing procedure X. If


we prefix X with F, then when X is called we first enter code belonging to
procedure E. This means that variables local to 7^ should be available tous,
and variables in the scope of F should also be available. However,
non-local variables belonging to procedure £ do not exist, since there is no
invocation oiF, and therefore some fraction ofthe static environment of
F is missing. This cannot be resolved, and so it is established thatX cannot
be prefixed by F here. Procedure B has the same problem.
In the program sketch shown in figure 1, the procedure named X
within procedure A has a prefix of " ?" which for now will indicate an
arbitrary prefix. To demonstrate the use of prefix identifiers from various
scopes, all possible values for a prefix on X will be examined in turn. The
procedures that could serve as a prefix for X, based on the previous
discussion about static environments, are A, C, D, E, and X, since
procedures F and B are not visible to X.
Proceeding with the simplest cases first, we would attempt to prefix X
with D. Since X and D are declared on the same lexical level, then this is
possible, and the result is obvious. Use of £ as a prefix is also allowed,
because E is declared in a block that encloses X, that block being
procedure A. In contrast, the use of X as a prefix on itself is recursive
prefixing and is therefore not permitted, even though scoping rules allow
PREFIXED PROCEDURES 257

it. C M^ould also be an example of recursive prefixing, since X exists in its


prefix chain, and is also illegal.
This leaves A as the only remaining prefix that has not been tried.
Notice that procedure X is completely enclosed by A. A peculiar set of
circumstances arises in this case, which is subtly different from recursive
prefixing. With recursive prefixing there is a difficulty in determining the
prefix level of the prefixed procedure. In this case, there is no such
problem—clearly the prefix level of procedure X is one. The prefix A on
procedure X also satisfies the above rule for the scope of prefix identifiers,
and so the prefixing of X by A is legal. However, some implementations of
prefixed procedures, especially pre-processors, will never terminate the
definition of procedure A.
If prefix identifiers could be passed as parameters, then the procedure
named B in figure 1 could be passed out of the enclosing block to the main
program, where it could be used as a prefix. Although there are
procedure and function parameters as part of the PASCAL syntax, the
SAHARA language does not permit them. Also, because procedure
parameters are only formal specifications, a pre-processor for PASCAL
could not properly generate code for the general case of parameter
prefixes. As a result, the implementations that exist allow no prefix
parameters.
Such an implementation would be possible with the restriction that,
from the prefixed procedure, variables that belong to the prefixing
procedure may not be accessed. This is because the compiler cannot know
which procedure will be passed and used as a prefix. The names of the
variables in the prefixing procedures are therefore not known and would
be considered undefined by a compiler.
This restriction has important consequences for the utility of prefix
valued parameters. Since a prefixed procedure is supposed to be an
extension of the original, access to the variables of the prefixing
procedure is often needed, procedure SET above, for example, could not
be passed as a parameter and used in any reasonable way.

IMPLEMENTATIONS
There are a few approaches to implementing prefixed procedures. The
two that I have practical experience with are the pre-processor imple-
mentation, and the inner table implementation, as was used in SAHARA.
The pre-processor technique is straightforward: a program reads the
user program, keeps track of all procedures, functions, variable declara-
tions, and inner statements, and generates a legal program with no
prefixed procedures, which is semantically equivalent. Such a pre-
258 J.R. PARKER
processor has been written for PASCAL at the University of Calgary. It
consists of over 1200 lines of PASCAL code and involves significant
overhead each time it is run. It is effectively a separate compiler pass that
does not profit from other passes.
There are severe restrictions associated with this approach that have
already been mentioned. The principal problem is that some forms of
nesting cannot be properly implemented. Also, the scope rules discussed
above are very difficult to accommodate, and prefix valued parameters
could not be a part of a pre-processor implementation.
The inner table technique was used in the programming language
SAHARA^^', the first language known to incorporate prefixed procedures.
The inner table solution involves a change to the compiler and run time
system for the language in question. All the information needed to
implement prefixed procedures is available at compile time. The addi-
tional complication is that of selecting the correct return address when a
procedure returns and of deciding where to pass control when an inner
statement is encountered. These addresses are available at compile time
and can be collected into a table for each procedure, which will be stored
in the heap. When a procedure is called, the relevant inner table address is
saved in a control area in the activation record where it can be referenced
for evaluation of an inner statement or return.
Figure 2 shows a possible structure for an inner table. If an inner
statement is encountered at prefix level N, then the action to take is to
branch to the address that is stored at
(Inner table address + iN*2) + 1).
For procedure X in figure 2, the prefix level is zero, so when the inner
statement is seen, we branch to the address found at inner table + 1, that
is, the location indicated by label XL The result is that nothing was done,
correct in this case. Repeating the process for the procedure Y gives the
results shown in the figure.
Procedure return is also performed through the inner table, but only if
the prefix level is non-zero. If the prefix level is zero, we return to the
location specified in the return address field in the activation record as
usual. Otherwise, we return to the location specified by
(Inner table address + iN*2)).
This backs out of one level of prefixing. All prefixed procedures begin
execution at the first line of code in the outermost (unprefixed) block.
The return addresses for prefixed procedures at levels > 1 are known
at compile time, and these entries in the inner table are simply copied into
the relevant areas of the activation record. The presence of these fields in
PREFIXED PROCEDURES 259

Inner table for procedure X:

PX
procedure X;
PX: begin
X\
inner;
XI:

end

Inner table for procedure Y:

PX
prefix X procedure Y
(* At inner level 1 *)
PY PY: begin

X\
inner;
Y\ Y\:

end;
Fxecution of Y proceeds as follows:
PX: begin

PY: begin

inner;
Y\:

end;(*ofF*)
XI:

end;(*ofX*)

FIG. 2. The inner table implementation

the run time environment, instead of a direct return to a fixed address,


permits the option of implementing prefix valued parameters and run
time execution traces and allows experimentation with other ideas such as
multiple inner statements and dynamic prefixing of procedures at
invocation time.
If an inner table implementation is used, the compiler can adjoin the
activation records of prefixed procedures so that new stack entries are not
required whenever an inner statement is seen. All the storage required for
all prefixes is allocated in one record, the size of which is known at compile
time. The SAHARA language does this and as a result needs to save static
link pointers in a local display area of the activation record.
260 J.R. PARKER
The overhead for calling a prefixed procedure in SAHARA was observed
to be only slightly greater than for simple procedure calls. Code is never
duplicated, but the activation records are larger and require more
information. The return from procedure requires one level of indirec-
tion; that is the lookup in the inner table. Few other sources of overhead at
run time can be identified. Even though an implementation exists, the
language SAHARA has other features that make it difficult to isolate the
effects of prefixing. Also, since the SAHARA language system is interpre-
tive, detailed timings may not be relevant.
Work using prefixing is continuing, but development of implementa-
tions is not. The development of Ada generic compilation units has
reduced the incentive, since many applications of prefixing can also use a
generic style of procedure.

REFERENCES
(1) O.J. Dahl, B. Myrhaug, and K. Nygaard, The Simula 67 Common Base Language,
Forkningsveien I B, Oslo 3: Norwegian Computing Center, 1968.
(2) E.W. Dijkstra, "Cooperating Sequential Processes," in F. Genuys, ed., Programming
Languages, New York: Academic Press, 1968, pp. 43-112
(3) J.R. Parker, "Language Design for System. Software Development," thesis, University
of Calgary, 1980
(4) J.G. Vaucher, "Prefixed Procedures: A Structuring Concept for Operations," INFOR,
13:3, 1975, 287-95

You might also like