label ::=gl? lidentifier:|ENTRY digit: lidentifier ::=identifier proceduredec ::=gl? PROCEDURE pidentifier (xacc ret?);statement; pidentifier ::=identifier ret ::=,integer gl ::=GLABEL: procedurestatement ::=lidentifier|pidentifier returnstatement ::=RETURN|RETURN(integer) gotostatement ::=GOTO lidentifier|GOTO pidentifier| GO TO lidentifier|GO TO pidentifier datastatement ::=DATA initial|DATA(initiallist) obeystatement ::=OBEY xcell
Normally a PLASYD program will be entered at the first statement following the initial declaration and the following statements will be obeyed in the order in which they appear, intervening declarations being skipped. If it is required to alter the order in which statements are obeyed, a control statement must be used. The simplest form of control statement is a jump to a different part of the program. In order to designate a statement to which control is to be passed, the statement must be labelled. The simplest way of doing this is to precede the statement by an identifier and a colon. The identifier must be unique within the program or section of program being compiled. It does, therefore, differ from the more usual Algol definition where the label definition acts as a declaration of that identifier as a label in the current block. For example:
BEGIN INTEGER I; BEGIN INTEGER J; ........ L:....... END; BEGIN INTEGER K; ........ L:..... END; END
This program is not allowed in PLASYD although it would be allowed in Algol.
There is no objection to a statement having several labels attached to it. Some examples are:
A:B:C:OLE:X3:=B(£B); L:X1:=X1+6; NEXTCH:B:=X4-X2; DB4:X1:=$NEST;
Accessing of labels from other separately compiled segments is described in Chapter 19.
The simplest way to transfer control unconditionally is the GOTO statement. Note that the word GOTO can be written as two words, GO TO if the user desires. The effect of this statement is to cause the statement designated by the label identifier and all statements following it to be obeyed in sequence until another control statement is reached. For example:
BEGIN INTEGER I, J; GOTO L; X0:=3; L:X1:=2; GOTO K; X2:=4; K:X3:=2; END;
This program will set X1 and X3 to 2, but will not alter the contents of X0 and X2.
The syntax also allows the GOTO statement to be used as a means of entering a procedure. This will be discussed in the section 13.5 concerned with procedure calling.
A procedure is a named group of PLASYD statements which may be obeyed at any point in a program by executing a procedure statement. After they have been obeyed control will normally return to the statement following the call. A procedure declaration specifies the group of statements that are to form the procedure and assigns a name to them. It also specifies an integer accumulator called the link. When the procedure is called from another part of the program the address of the next instruction to the call will be placed in this link accumulator. Procedure declarations, like other declarations, must appear at the head of a block. The scope of the procedure name is, however, similar to that for a label. The procedure name may not be used elsewhere within the program. It is possible to call a procedure at a point in the program before the declaration if required, and even to call it from outside the block in which it is declared. Communication between the rest of the program and a procedure can be via any of the variables declared in the outer block or the accumulators. There is, therefore, little reason for defining procedures at any level other than the outer block level. It is possible to define procedures as separately compiled segments which can be accessed via global variables. Full details of these will be given later.
A simple example of a procedure declaration is:
PROCEDURE ZERO (X1); BEGIN A1:=0.0; X0:=0; END;
The identifier ZERO is the name by which this procedure will be known. The link accumulator in this case is X1. Consequently, a call of the procedure ZERO will set X1 to point to the next statement after the procedure statement which made the call. The result of calling this procedure will be to set the real accumulator and the first integer accumulator to zero. At the end of the procedure control will transfer back to the instruction specified by X1. The body of the procedure declaration is a single statement which will normally be a block. However, it can be a single statement. For example:
PROCEDURE ZERO (X1); X0:=0;
is allowable.
A procedure may be called at any point in the program by executing a procedure statement. This is a simple statement consisting merely of the procedure identifier. For example:
ZERO;
would cause the procedure defined in the previous section to be entered. The effect of the procedure statement is to store the address of the next instruction in the link accumulator and then to transfer control to the statement that forms the body of the procedure. This will usually be a block. When this statement has been executed, providing the link accumulator has not be changed, control will normally pass to the statement following the call and proceed in normal sequence from there.
If the link accumulator needs to be used for other purposes in the body of the procedure, its entry value should be stored and then recovered before exit. Exit from the procedure body is achieved:
For example:
PROCEDURE ZERO(X1); BEGIN A1:=0.0; END; PROCEDURE ZERO(X1); BEGIN A1:=0.0; GOTO LAST; ..... LAST:END; PROCEDURE ZERO(X1) BEGIN A1:=0.0; RETURN .... END;
All three procedures set the real accumulator to zero and exit.
One method of passing data between the calling program and the procedure is by using the accumulators and variables declared in the outer block. However, there is no standard method of passing parameters and the programmer may use any method he likes. The standard method adopted in PLAN programs is to follow the procedure call by statements to load the integer accumulator X3 with the addresses of the parameters.
Some means must be provided to access this statement. The OBEY statement causes the 1900 machine code instruction stored in the integer cell specified to be obeyed. If it does not cause a change of control, then, after execution, control will return to the statement following the OBEY. Consider the following program:
BEGIN REAL F,G; PROCEDURE SQUARE (X1); BEGIN OBEY(X1); A1:= (X3)*(X3); X1:=X1+1; END; X2:=£F; A1:=3.0; F(X2):=A1; SQUARE; X3:=@F; RET:G(X2):=A1; END
Before calling the procedure SQUARE, the variable F is set equal to 3.0. The procedure SQUARE executes the statement stored in the position pointed at by the link accumulator. This is the action of the OBEY statement. The next statement sets the real accumulator to the square of the argument. Before leaving the procedure SQUARE, the integer accumulator X1 is increased by 1 so that the link now points at the statement labelled with RET. On returning from the procedure SQUARE, G is set equal to the value of the real accumulator (9.0 in this case).
The statement X1:=X1+1 in the previous section can be omitted if the procedure SQUARE is defined with the second integer argument. The second argument causes the control to be returned to the address of the instruction following the procedure call incremented by the value of the integer.
For example:
PROCEDURE SQUARE (X1,1);
would mean that the incrementing of X1 would not now be necessary. In general, the integer value will be equal to the number of arguments to the procedure. This will ensure that the instruction following the arguments is next obeyed on return from the procedure.
An alternative method of specifying a different return address from the instruction following the procedure call, is to exit from the procedure using the RETURN statement with an integer argument. This has the same effect as the second argument in the procedure declaration. If a procedure is declared with the second argument n and a RETURN statement with an argument m then the RETURN takes precedence and control is returned to m instructions past the one following the procedure call. If RETURN has no arguments, control is returned to n instructions past the one following the procedure call. For example:
PROCEDURE SQUARE (X1,2); BEGIN ...... A:RETURN; ..... B:RETURN(3); ....... C:END;
The return address in the three different exit positions is:
A:X1+2 B:X1+3 C:X1+2
datastatement ::=DATA initial|DATA(initiallist) initiallist ::=initial|initial*integer|initiallist,initial| initiallist, initial*integer initial ::=xinitial|rinitial|xxinitial|rrinitial αinitial ::=αvalue {α=r|xx|rr} xinitial ::=xvalue|string|fixedaddress|functionstatement| count+fixedaddress|count+integer|count+truncated| count+octalinteger string ::="stlist" stlist ::=st|stlist st st ::=char|" "| |' fixedaddress ::=basicfixed|CHAR integer|CHAR integer OF basicfixed| @identifier|@lidentifier basicfixed ::=@fixedcell|$fixedcell|£identifier fixedcell ::=xfixedcell|xxfixedcell|rfixedcell|rrfixedcell αfixedcell ::=αidentifier|αidentifier(integer)|αidentifier(-integer) {α=r|xx|rr}
The DATA statement provides an alternative means of passing arguments to procedures. Its effect is to store the initial values at that point in the program. In its simplest form, the DATA statement allows constant values of all types to be stored. For example:
DATA 3; DATA (3.7,1,57D,27.3L);
Particular constants may be repeated a number of times using the initial*integer form.
For example:
DATA (3.7,1*5,2.3);
This will cause the constants 3.7,1,1,1,1,1,2.3 to be stored at that point in the program. The integer following the * indicates the number of times the constant is to be repeated. As well as constants, the following integer values are allowed:
6CNT+3, 17CNT+@I, 5CNT+$J(3), 12CNT+MINUS3, 5CNT+27T2, 7CNT+#22
!LDN(X1,4) and !FAD(3,B)
The previous example of the procedure SQUARE could be modified so that the value to be squared is contained in the location following the procedure call as follows:
BEGIN REAL G; PROCEDURE SQUARE(X1,2); BEGIN A1:=(X1)*(X1); END; SQUARE; DATA(3.0); X2:=£G; G(X2):=A1; END;
Note that as the data item is a real quantity, it will take up two locations and consequently the return address must be incremented by 2 using the second argument in the procedure declaration. A more complicated example might be:
XXX; DATA(1,3.0,22D,3.5L,"ABCDE");
Here the return address could have to be incremented by 11 (1+2+2+4+2).
Procedures should not directly or indirectly call themselves since recursion is not catered for directly by the PLASYD system. If recursion is necessary the user must make his own arrangements to stack the link unless return down the recursion chain is not needed. In this case the final procedure must be left by a GOTO jump to a label external to the procedure.
Any procedure, whether recursive or not, can be left by a GOTO statement. Care must be taken in the case when the label jumped to is in a block parallel to the one in which the procedure is declared. As storage is reallocated, values of variables could be lost. This also applies in the case where a procedure is called from a block not enclosed by the one in which the procedure is declared. It is good practice only to call procedures from the one in which the procedure is declared or a block enclosed by this procedure. Consider, for example:
BEGIN INTEGER I; U:BEGIN INTEGER J,K; PROCEDURE FRED(X1); BEGIN X2:=£J; J(X2):=0; .......... GOTO LB; .......... RETURN; .......... END; ........ A:FRED; ......... END; ....... V:BEGIN INTEGER L,M; ........ B:FRED; ........ LB:......... END; END
Here the procedure FRED is defined within the block labelled U and called from both this block and the parallel block labelled V. Exit from the procedure is either to the label LB or a standard return. If we first consider the call of FRED labelled A, the standard return will have the variable J available and set to zero. If the procedure is left by the GOTO then the return address will be in block V and the variable J will now not be available and the fact that it had been set to zero would not be known. If the procedure had been called from the statement labelled B in block V then either the normal return or the GOTO exit will be back to the block V. However, the blocks U and V may well share storage for the variables J, K and L, M. Consequently, the updating of the value of J by the procedure FRED may well have altered the value of the variable L.
In order to allow recursion of PLASYD procedures, the user must set up a stack to hold the contents of the link accumulators. A simple system could be organised where all procedures use the same link accumulator and the only procedure exits must be standard returns and not GOTO jumps. In this case a single stack will be all that is required. For example:
BEGIN LOWER INTEGER STACK(100),STACKPTR=1; LOWEND; PROCEDURE A(X1); BEGIN X2:=STACKPTR; STACK(X2):=X1; STACKPTR:=X2+1; .............. X2:=STACKPTR-1; X1:=STACK(X2); STACKPTR:=X2; END; ......... END
If all procedures have the same statements at the head and return position as procedure A, then they may be called recursively up to a maximum depth of 100.
Labels and procedures are to a large extent interchangeable in PLASYD. A procedure call always sets up the link value while a GOTO jump does not. It is possible for both to enter a procedure by a GOTO jump or to enter at a label within a procedure by a procedure call.
The first statement of the body of a procedure may be jumped to by a GOTO statement. No link will be stored and if the end of the procedure body is reached or a RETURN statement is executed then the next statement obeyed will be determined by the contents of the link accumulator at that point. The main use of the facility is, therefore, when a string of procedures is to be obeyed with no return from each required.
Another place in which this facility can be of use is when a procedure calls a subprocedure and, on return from this subprocedure, immediately leaves the main procedure. In this case a GOTO the subprocedure will, when the return is reached, exit to the place from which the main procedure was called assuming both procedures use the same link. This is more efficient than a standard procedure call to the subprocedure. For example:
PROCEDURE CALL GOTO CALL BEGIN BEGIN PROCEDURE MAIN(X1); PROCEDURE MAIN(X1); BEGIN; BEGIN; SUB; GOTO SUB; END; END; PROCEDURE SUB(X3); PROCEDURE SUB(X1); BEGIN BEGIN X2:=0; X2:+0; END; END; MAIN; MAIN; END END;
A label declared in the body of a procedure may be treated as a procedure with the same link as the procedure itself. This can be used to provide alternative entry points to a procedure. For example:
PROCEDURE SIN(X1); BEGIN ........ COS:.... ........ END;
Here both SIN; and COS; are statements that can appear in the program. The return action will be the same in both cases.