# Procedure declarations

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
- Type-bound procedures
- Predeclared procedures
- Function procedures
- Proper procedures

## Formal parameters

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

## Type-bound procedures

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;

## Predeclared procedures

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.

## Function procedures

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

## Proper procedures

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.