modula-2 home

  Home  
  Tutorial  
  Win32 API  
  Reference  
  Projects  
 

 

Procedure declarations

Procedures are sequences of instructions that perform a specific task. Usually, procedures are used when the same sequence of code is required more than once in a program.  In this case, the procedure is written once and is called from several places.

Procedures also improve the clarity of a program.  A complicated task is easier to understand if it is composed of a sequence of sub tasks, each implemented by a procedure.

To make procedures more flexible, they can take parameters, which are data objects that can vary from one call to the next.  This lets you specify an algorithm with a procedure that is run with different data at different times.

Procedure Declarations

The procedure declaration takes the following form:

PROCEDURE identifier [(Formals) [:ReturnType]]; [ FORWARD ]
 [Body]

The identifier is the name of the procedure.

Formals are specified by:

[FormalSection {; FormalSection}]

where FormalSection is:

[VAR identifier {,identifier}: [ARRAY OF {ARRAY OF}] FormalType

FormalType is a type name, and may be qualified by a module name.

If ReturnType is specified, the procedure is a function procedure.  A function procedure is a procedure that returns a value, and can be used in expressions.  ReturnType is the name of the type of value that the procedure returns.

Note that on function procedures, the parentheses of the parameter list are required even if there are no parameters.

Body takes the following form:

{declaration}[BEGIN ListOfStatements [EXCEPT ListOfStatements]] END identifier;

The body of the procedure is a block that is executed when the procedure is called.  The normal execution body consists of the ListOfStatements between the BEGIN keyword and the EXCEPT keyword if present or the END keyword if EXCEPT is not present. If the EXCEPT keyword is present then the ListOfStatements between the EXCEPT keyword and the END keyword for the procedure are the exception execution body of the procedure. If the procedure declaration is in a definition module, the body is not included.

The identifier at the end of the body must be the same as the name of the procedure.

Examples:

The following procedure takes no parameters and does not return a value.

PROCEDURE Error;
   BEGIN
       WriteString('ERROR');
   END Error;

The following procedure takes the parameter x and returns a REAL value.

PROCEDURE tan(x : REAL) : REAL;
   BEGIN
       RETURN sin(x) / cos(x);
   END tan;

FORWARD

The FORWARD modifier is not allowed in DEFINITION modules and it signals that the procedure header is a forward declaration of the full procedure declaration which follows later in the source code. This keyword exists in the language to support single pass compilers.

PROCEDURE add(a, b : CARDINAL) : CARDINAL; FORWARD;

 

... (* other code *)

 

PROCEDURE add(a, b : CARDINAL) : CARDINAL;

BEGIN

    RETURN a + b;

END add;

The Stony Brook compiler is a two pass compiler and therefore never requires the use of the FORWARD keyword.

Formal Parameters

The parameters of a procedure are variables that are declared in the procedure header. These variables are referred to as the formal parameters.  The formal parameters can be referred to in the body of the procedure.

When you call a procedure you must supply a variable or expression for each of the formal parameters.  These are called the actual parameters.

See the section Function calls in the Expressions chapter of this book and the section Procedure calls in the Statements chapter of this book for more information on calling procedures and actual parameters.

There are two classes of formal parameters: value parameters and variable parameters.  A formal parameter is a value parameter unless the VAR keyword precedes it in the procedure declaration.

Value parameters

Value parameters act as if they are local variables that are assigned an initial value when the procedure is called.  The initial value is the value of the actual parameter.

Because the relationship between the actual parameter and the formal parameter is an assignment, the type of the actual parameter must be assignable to the type of the formal parameter.

The actual parameter associated with a value parameter can be either a variable or an expression.  If it is a variable, assignments to the formal parameter inside the procedure have no effect on the value of the actual variable.

Variable parameters

The actual parameter associated with a variable parameter must be a variable whose type is identical to that of the formal parameter.  The formal parameter refers to the same variable as the actual, so assignments to the formal parameter also change the value of the variable that is passed as an actual parameter.

You use variable parameters when you want the procedure to alter the value of the actual parameter passed to it, for example, to return a new value.

You should also use variable parameters for array and record types, whenever possible because they can be more efficient.  When you pass a value parameter, it must be copied, and copying of large arrays and records can be time consuming.

Example:

PROCEDURE Next(a : CHAR; VAR b : CHAR);
   BEGIN
       INC(a);
       b := a;
   END Next;

VAR
   X, Y : CHAR;
BEGIN
   X := 'A';
   Y := 'Z';
   Next(X, Y);
END

After the call to Next, X will have the value 'A' and Y will have the value 'B'.  X is unchanged even though the associated formal parameter is changed, since it is a value parameter.  Y is changed by the procedure since it is associated with b, a variable parameter.

Open array parameters

You can pass array types as parameters to a procedure by declaring a formal parameter with the type name of an array type.  This method, however, is inflexible, since the actual parameter must be exactly the same type as the formal, including the same index type and bounds.

To pass arrays of varying sizes to a procedure, you can declare the formal parameter like this:

ARRAY OF {ARRAY OF} ElementType

Open array parameters can have any number of dimensions, for now just consider an open array parameter with a single dimension. The rules for multi dimension open array parameters logically extend from the single dimension rules.

When the formal parameter is declared this way, you can pass any single dimensional array having the same ElementType, regardless of the index type or range.

Inside the procedure, array formals are indexed by cardinals from 0 to the number of elements in the actual array minus 1, regardless of the type and range of the index of the actual parameter.

You can use the standard procedure HIGH to get the upper bound of the array formal.  It returns a CARDINAL value, which is the number of elements in the actual parameter minus one.

Example:

PROCEDURE WriteStr(s : ARRAY OF CHAR);
VAR
   i : CARDINAL;
BEGIN
   FOR i := 0 TO HIGH(s) DO
       Write(s[i]);
   END;
END WriteStr;

You can pass any single dimensional array of characters to this procedure.  For example:

VAR
   message : ARRAY [1..10] OF CHAR;

In this case, the formal parameter will be indexed from 0 to 9, even though the actual was indexed from 1 to 10.

Let us now consider two dimensional open array parameters.

If a formal parameter is a two dimensional array of type REAL then you can pass any two dimensional array of type REAL to this parameter. The rules regarding the range of array subscripts are the same as for one dimensional open array parameters.

You can use the standard procedure HIGH to get the upper bound of each dimension of the array formal.  It returns a CARDINAL value, which is the number of elements in specified dimension of the actual parameter minus one.

Example:

PROCEDURE MatrixAdd(VAR c : ARRAY OF ARRAY OF REAL;

  a, b : ARRAY OF ARRAY OF REAL);
VAR

  i, j : CARDINAL;
BEGIN
   IF (HIGH© = HIGH(a)) AND (HIGH(c) = HIGH(b)) AND
      (HIGH(c[0]) = HIGH(a[0])) AND (HIGH(c[0]) = HIGH(b[0]))
   THEN
       FOR i := 0 TO HIGH(c) DO
           FOR j := 0 TO HIGH(c[0]) DO
               c[i, j] := a[i, j] + b[i, j];
           END;
       END;
   ELSE
       FatalError('Bozo');
   END;
END MatrixAdd;

The rules we have just discussed for one a two dimensional open array parameters are extended for arrays of any dimension. For more information on the HIGH function see the standard procedure chanter of this document.

Function Procedures

Procedures that return a value are called function procedures.  Function procedures can be called inside an expression, and the value returned is substituted for the call.

You declare a function procedure by including the ReturnType in the procedure declaration.

The type returned by a function procedure can be any type.

Inside a function procedure, there must be at least one RETURN statement.  The RETURN statement takes the following form:

RETURN expression

The expression must be assignable to the type declared as the return type of the procedure.  The RETURN statement terminates the execution of the procedure, returning the value of the expression as the value of the function.

Local Variables

Any declarations included in the body of a procedure are local to the procedure.  The names are not defined outside the procedure body.

Variables declared in a procedure body are known as local variables. Space is allocated for these variables only when the procedure is executed.

Each time the procedure is executed, the values of the local variables are undefined.  Local variables do not retain their values from one call of the procedure to the next.

Modula-2 procedures can be called recursively.  The procedure can be called from its own body, or by other procedures called from its body.

Each recursive call of a procedure allocates a new set of local variables.  Changes to a variable in one invocation of the procedure have no effect on the values in other invocations.


Source:

  • Stony Brook Modula-2 documentation. Used with permission. Note: Stony-Brook M2 offers an extended syntax with features not described here. Stony Brook M2 users are encouraged to visit the Stony Brook website and to consult the Stony Brook help system.