t3x.org / t3x / t3x-manual / 28.html
 
T3X - A Minimum Procedural Language
Version 8.1.5, Online Edition
Copyright(C) 1996-2004
Nils M Holm
 
Previous:
2.7.8 Empty Statements
TOC | Index | Back Next:
2.8.1 Recursive Procedures

2.8 Procedures

Each procedure may be considered a separate small program. It communicates with other procedures using parameters and return values and/or via global variables. Each procedure has access to all global and class-level data objects which have been declared before itself. Generally, it is considered good style to keep procedures self-contained in procedural languages and use global storage as little as possible, but when data has to be shared between a big number of different procedures, the use of top-level definitions is very common and more efficient.

The definition of a procedure has only one single form in T3X. Since there is no support for nested routines, all procedure declarations and definitions must occur at the top level (the space between the other global declarations) or in class contexts. Public procedures declared in class contexts are called methods. They will be explained later.

The only form of the procedure definition is

P(a1, ... aN) statement

where P is the name of the procedure, a1...aN are the names of is formal arguments, and statement is the body of the procedure - the part which describes its meaning. In most cases, the statement is a block statement:

P(a1, ... aN) DO
    declarations
    statements
END

The procedure name may be any valid symbol and it is declared in the global context or in a class context. The names of procedures declared at global level may not be reused ever. (One advantage of T3X's strict scoping rules is that procedures cannot get shadowed.) The arguments a1...aN are local to the procedure (not local to the statement forming the body). Their names will cease to exist after parsing the statement. Hence, they may be reused in subsequent procedure definitions, but not inside of the current one. The parentheses around the argument list must always be specified, even if the list is empty:

Q() statement

The number of arguments specified in a procedure definition determines the type of the procedure. The type of a procedure is an integer number which represents the number of its arguments. In T3X, the number of actual arguments in procedure calls is checked against the type of the called procedure. The compiler will not allow calls with a wrong number of parameters. This is done because of T3X's calling conventions: Parameters are passed in reverse order and therefore, each procedure relies on a correct number of arguments. BCPL and C, for example, use a different approach in which it is easy to compensate for missing or superflous procedure parameters. The advantage of this approach is that it makes the definition of procedures with a variable number of arguments (so-called variadic procedures) easier. In T3X, variadic procedures are implemented by using dynamic tables.)

When a procedure is called, it may receive data through its arguments. This works in the following way. Given a procedure

P(x, a, b, c) RETURN a*x*x + b*x + c;

and a procedure call

Q() DO VAR y; y := P(2, 3, 5, -7); END

the caller (Q) places the values of the actual arguments 2, 3, 5, and -7 in a temporary storage location (usually on the runtime stack), saves the address of the following operation (in this case the assignment) and then transfers control to the procedure P. In P, the formal arguments x, a, b, and c reference storage locations which exactly match the temporary locations of the values passed to the routine, so that x=2, a=3, b=5, and c=-7.

The procedure P computes a*x*x+b*x+c and returns the resulting value to the caller. Each procedure returns automatically when its body has been processed completely or when an explicit RETURN statement is executed. In the above example, both happens at the same time. It is not unusual to specify a RETURN statement at the end of a procedure, since only RETURN may pass an explicit value back to the caller. Procedures which do not return through RETURN have an implicit return value of zero. In the example, however, the value of P is specified explicitly. After passing control back to the caller, the assignment takes place, and the result of the procedure call is stored in y. Between the procedure return and the assignment, the temporary storage, where the actual arguments were held, is released.

Procedure arguments are passed by value. This means that the value of each parametric expression is passed to a procedures. In the statement

F(x+10);

for example, x+10 is computed first and the resulting value is passed to F. Modifying the argument in F does not affect the actual argument value, since a copy of the argument value is passed to the procedure:

f(x) x := 27;   ! modify local copy of x

DO VAR x, y;
        x := 9;
        f(x);   ! pass x to f()
        y := x; ! x is still 9
END

Vector arguments are also passed by value, but since the value of a vector is in fact a reference, vectors are effectively passed by reference. For example, the procedure call

truncate("This string is too long", 10)

passes the address of the string "This string is too long" to the procedure truncate. If truncate modifies its first argument, it actually does modify the string literal passed to it. Given the following definition of truncate:

truncate(s, i) s::i := 0;

the above procedure call would truncate the string argument to "this strin". Notice that truncate does not have to return the modified string. The modification affects the same copy of the string as included in the argument, thereby effectively turning the calling expression into

truncate("This strin", 10)

The modification of the string is a side effect of the truncate procedure.

As indicated before, the most frequently used form of the procedure has a body consisting of a compound statement:

fib(n) DO VAR f, i, j, k;
        f := 1;
        j := 1;
        FOR (i=1, n) DO
                k := f;
                f := j;
                j := j+k;
        END
        RETURN f;
END

Note: The variables declared at the beginning of the procedure

VAR f, i, j, k;

belong to the compound statement rather than to the procedure. Like in conditional statements and loops, the statement block is used to extend the scope of the procedure: not just a simple statement, but a group of statements forms the body of the routine.

Previous:
2.7.8 Empty Statements
TOC | Index | Back Next:
2.8.1 Recursive Procedures