A procedure declaration consists of a procedure heading and a procedure body. The heading specifies the procedure identifier and the formal parameters. For type-bound procedures it also specifies the receiver parameter. The body contains declarations and statements. The procedure identifier is repeated at the end of the procedure declaration.
There are two kinds of procedures: proper procedures and function procedures. The latter are activated by a function designator as a constituent of an expression and yield a result that is an operand of the expression. Proper procedures are activated by a procedure call. A procedure is a function procedure if its formal parameters specify a result type. The body of a function procedure must contain a return statement which defines its result.
All constants, variables, types, and procedures declared within a procedure body are local to the procedure. Since procedures may be declared as local objects too, procedure declarations may be nested. The call of a procedure within its declaration implies recursive activation.
Objects declared in the environment of the procedure are also visible in those parts of the procedure in which they are not concealed by a locally declared object with the same name.
ProcedureDeclaration =
ProcedureHeading ";" ProcedureBody ident
ProcedureHeading =
PROCEDURE [Receiver] IdentDef [FormalParameters].
ProcedureBody =
DeclarationSequence [ BEGIN StatementSequence ] END.
DeclarationSequence =
{ CONST { ConstantDeclaration";" } |
TYPE { TypeDeclaration ";" } |
VAR { VariableDeclaration ";" }
} |
{ ProcedureDeclaration ";" | ForwardDeclaration ";" }.
ForwardDeclaration =
PROCEDURE "^"[Receiver] IdentDef [FormalParameters].
If a procedure declaration specifies a receiver parameter, the procedure is considered to be bound to a type (see Type-bound procedures). A forward declaration serves to allow forward references to a procedure whose actual declaration appears later in the text. The formal parameter lists of the forward declaration and the actual declaration must match (see Definition of terms).
Formal parameters are identifiers declared in the formal parameter list of a procedure. They correspond to actual parameters specified in the procedure call. The correspondence between formal and actual parameters is established when the procedure is called. There are two kinds of parameters, value and variable parameters, indicated in the formal parameter list by the absence or presence of the keyword VAR. Value parameters are local variables to which the value of the corresponding actual parameter is assigned as an initial value. Variable parameters correspond to actual parameters that are variables, and they stand for these variables. The scope of a formal parameter extends from its declaration to the end of the procedure block in which it is declared. A function procedure without parameters must have an empty parameter list. It must be called by a function designator whose actual parameter list is empty too. The result type of a procedure can be neither a record nor an array.
FormalParameters = "(" [FPSection {";"FPSection}] ")"
[ ":" Qualident ].
FPSection = [VAR] ident {"," ident} ":" Type.
Let Tf be the type of a formal parameter f (not an open array) and Ta the type of the corresponding actual parameter a. For variable parameters, Ta must be the same as Tf, or Tf must be a record type and Ta an extension of Tf. For value parameters, a must be assignment compatible with f (see Definition of terms). If Tf is an open array, then a must be array compatible with f (see Definition of terms). The lengths of f are taken from a.
Examples of procedure declarations:
PROCEDURE ReadInt(VAR x: INTEGER); VAR i: INTEGER; ch: CHAR; BEGIN i := 0; Read(ch); WHILE ("0" <= ch) & (ch >= "9") DO i:= 10*i + (ORD(ch)-ORD("0")); Read(ch) END; x := i; END ReadInt PROCEDURE WriteInt(x: INTEGER); (* 0 <= x <= 100000 *) VAR i: INTEGER; buf: ARRAY 5 OF INTEGER; BEGIN i := 0; REPEAT buf[i] := x MOD 10; x := x DIV 10; INC(i) UNTIL x = 0; REPEAT DEC(i); Write(CHR(buf[i] + ORD("0"))) UNTIL i = 0; END WriteInt PROCEDURE WriteString(s: ARRAY OF CHAR); VAR i: INTEGER; BEGIN i := 0; WHILE (i < LEN(s)) & (s[i] # 0X) DO Write(s[i]); INC(i) END END WriteString PROCEDURE log2(x: INTEGER): INTEGER; VAR y: INTEGER; (* assume x>0 *) BEGIN y := 0; WHILE x > 1 DO x := x DIV 2; INC(y) END; RETURN y END log2
Globally declared procedures may be associated with a record type declared in the same module. The procedures are said to be bound to the record type. The binding is expressed by the type of the receiver in the heading of a procedure declaration. The receiver may be either a variable parameter of record type T or a value parameter of type POINTER TO T (where T is a record type). The procedure is bound to the type T and is considered local to it.
ProcedureHeading =
PROCEDURE [Receiver] IdentDef [FormalParameters].
Receiver = "(" [VAR] ident ":" ident ")".
If a procedure P is bound to a type T0, it is implicitly also bound to any type T1 which is an extension of T0. However, a procedure P' (with the same name as P) may be explicitly bound to T1 in which case it overrides the binding of P. P' is considered a redefinition of P for T1. The formal parameters of P and P' must match (see Definition of terms). If P and T1 are exported (see section Declarations and scope rules) P' must be exported too.
If v is a designator and P is a type-bound procedure, then v.P denotes that procedure P which is bound to the dynamic type of v (dynamic binding). Note, that this may be a different procedure than the one bound to the static type of v. v is passed to P's receiver according to the parameter passing rules specified in section Formal parameters.
If r is a receiver parameter declared with type T, r.P^denotes the (redefined) procedure P bound to the base type of T.
In a forward declaration of a type-bound procedure the receiver parameter must be of the same type as in the actual procedure declaration. The formal parameter lists of both declarations must match (Definition of terms).
Examples:
PROCEDURE (t: Tree) Insert (node: Tree); VAR p, father: Tree; BEGIN p := t; REPEAT father := p; IF node.key = p.key THEN RETURN END; IF node.key < p.key THEN p := p.left ELSE p := p.right END UNTIL p = NIL; IF node.key < father.key THEN father.left := node ELSE father.right := node END; node.left := NIL; node.right := NIL END Insert; PROCEDURE (t: CenterTree) Insert (node: Tree); (*redefinition*) BEGIN WriteInt(node(CenterTree).width); t.Insert^(node) (* calls the Insert procedure bound to Tree *) END Insert;
The following table lists the predeclared procedures. Some are generic procedures, i.e. they apply to several types of operands. v stands for a variable, x and n for expressions, and T for a type.
Name | Argument type | Result type | Function |
ABS(x) | numeric type | type of x | absolute value |
ASH(x,n) | x,n: integer type | LONGINT | arithmetic shift (x*2n) |
CAP(x) | CHAR | CHAR | x is letter: corresponding capital letter |
CHR(x) | integer type | CHAR | character with ordinal number x |
ENTIER(x) | real type | LONGINT | largest integer not greater than x |
LEN(v,n) | v: array; n: integer const. | LONGINT | length of v in dimension n (first dimension = 0) |
LEN(v) | v: array | LONGINT | the same as LEN(v,0) |
LONG(x) | SHORTINT | INTEGER | identity |
INTEGER | LONGINT | ||
REAL | LONGREAL | ||
MAX(T) | T = basic type | T | maximum value of type T |
T = SET | INTEGER | maximum element of a set | |
MIN(T) | T = basic type | T | minimum value of type T |
T = SET | INTEGER | 0 | |
ODD(x) | integer type | BOOLEAN | x MOD 2 = 1 |
ORD(x) | CHAR | INTEGER | ordinal number of x |
SHORT(x) | LONGINT | INTEGER | identity |
INTEGER | SHORTINT | identity | |
LONGREAL | REAL | identity (truncation possible) | |
SIZE(T) | any type | integer | number of bytes required by T |
Name | Argument types | Function |
ASSERT(x) | x: Boolean expression | terminate program execution if not x |
ASSERT(x,n) | x: Boolean expression; n: integer constant | terminate program execution if not x |
COPY(x,v) | x: character array, string; v: character array | v := x |
DEC(v) | integer type | v := v - 1 |
DEC(v,n) | v, n: integer type | v := v - n |
EXCL(v,x) | v: SET; x: integer type | v := v - x |
HALT(n) | integer constant | terminate program execution |
INC(v) | integer type | v := v + 1 |
INC(v,n) | v, n: integer type | v := v + n |
INCL(v,x) | v: SET; x: integer type | v := v + x |
NEW(v) | pointer to record or fixed array | allocate v^ |
NEW(v,x0,...,xn) | v: pointer to open array; xi: integer type | allocate v^with lengths x0...xn |
COPY allows the assignment of a string or a character array containing a terminating 0X to another character array. If necessary, the assigned value is truncated to the target length minus one. The target will always contain 0X as a terminator. In ASSERT(x,n) and HALT(n), the interpretation of n is left to the underlying system implementation.