Program Structure

A Pascal program takes the following form.

program name (file-variables);
label lab, lab, ... ;
const
    name = constant;
    { Other constant declarations }
type
    name = type;
    { Other type declarations }
var
    name : type;
    { Other variable declarations }
{ Procedure and function declarations }
begin
    statements
end.

We have already discussed the first four declaration sections and the possible statements that can appear in the body of the program. We will now go on to describe the other parts of the Pascal program.

The program Statement

Traditionally, every working Pascal program begins with a program statement. With Alice, the program statement will always be present, but it has absolutely no effect on how the program behaves. Alice ignores everything on the program statement.

The program statement has the form

program name(file-variables);

The name is the name of the program. The file-variables that are enclosed in parentheses in the program template are input and output. These are just there for form -- their presence means nothing. More file variables may be added to this list, but this has no meaning to the program.

All declarations following the program statement (before any function or procedure declarations) are global to the entire program.

Procedure Declarations

The general form of a procedure declaration is

procedure name(parameter declarations);
label
    label-declarations
const
    constant-declarations
type
    type-declarations
var
    variable-declarations
procedure and function declarations
begin
    Code for procedure
end;

This is almost exactly the same format as the program itself. The various declaration sections declare identifiers that are local to this particular procedure. This items will not be available to subprograms that are defined outside this procedure.

The names will also mask any names declared in subprograms that enclose the procedure. For example, suppose that procedure A contains the declaration of procedure B and both declare a variable i. Inside B, the identifier i will refer to B's version of this variable; outside B, the identifier i will refer to A's version of the variable. Operations performed on i in B will not affect the value of i in A.

The parameter declarations at the beginning of the procedure declaration give the names and the types for the parameters that the procedure accepts. The parameter names are local and mask any identical names declared outside the procedure. Simple parameter declarations can have any of the following forms.

name : type;

name, name, ... : type;

var name : type;

var name, name, ... : type;

For example, a typical procedure declaration might begin with

procedure example( k : integer;
                   x, y : real;
                   var c : char);

Parameter declarations without the word var are called value parameters. When the procedure is called, these value parameters will receive a copy of the argument value passed by the caller. This process is called call by value. The argument passed by the caller may be any expression of an assignment compatible type, because passing the argument is like assigning a value to the parameter.

Because the procedure only has a copy of these parameters, operations that the procedure performs on the parameters will not effect the corresponding arguments in the caller. For example, consider the following procedure.

procedure fakeswitch(a,b:integer);
var
    temp : integer;
begin
temp := a;
a := b;
b := temp;
end;

This procedure switches the values of its parameters, but the switch has no effect on the values that the caller passes. Since fakeswitch is only working with copies of the caller's arguments, nothing can change in the caller.

Parameter declarations that start with the word var are called var parameters. When the procedure is called, the caller will pass the address of each argument instead of the value of the arguments. Through these addresses, the parameters are directly associated with the caller's arguments. This process is called call by reference. Any operations that the procedure performs on the parameters are actually performed on the arguments passed by the caller. For this reason, the arguments passed by the caller must be variables with exactly the same type as the parameter -- the parameter and the argument are exactly the same thing. As an example, consider the following procedure.

procedure trueswitch(var a,b:integer);
var
    temp : integer;
begin
temp := a;
a := b;
b := temp;
end;

This procedure switches the values of its parameters, and thereby switches the values of the arguments that were passed to the procedure. The var parameters are identified with the arguments passed by the caller. The arguments must be integer variables. This avoids absurdities like

trueswitch(2,3);

which would attempt to switch the values of the constants 2 and 3.

When a parameter is declared as a var parameter, the argument passed by the caller may be an array element or a record field as well as a normal variable. Note that some versions of Pascal do not let you pass an element of a packed structure when the argument corresponds to a var parameter. For example,

var
    str : packed array[1..100] of char;
    ...
proc(str[1]);

would be illegal if the parameter for proc was a var parameter.

Arguments to a procedure may have any type. If you are passing a record or array as a value argument, it is not necessary to initialize every field in the record or every element in the array. You will, however, receive an error if the called routine tries to use an uninitialized record field or array element.

Generic Type Parameters

Alice supports a special kind of parameter, of type generic. A generic parameter may be of any type, and is passed to the procedure as a record containing information about the size and type of the parameter (as well as a pointer to the actual parameter that was passed). The format for the generic type record is:

generic = record
    Object : pointer;   { pointer to the actual parameter }
    Size : integer;     { size of the variable in bytes }
    TypeCode : integer; { the type of the parameter 9see below) }
    end;

TypeCode is one of:

1enum, boolean, char, byte or small subrange of integer
2integer or subrange
3pointer (in which case, size is the size of the object pointed to)
4real number
5set
6string (i.e. packed array [1..n] of char); size is length of string + 2
10array variable (other than a string)
11file
12record

Function Declarations

Function declarations are almost exactly like procedure declarations. The difference is that the first line has the form

function name(parameter declarations):type;

The type that comes after the colon at the end of the line is the type of value returned by the function.

To set a return value for the function, you should assign the return value to the function name, as in

function iabs(i:integer):integer;
begin
if i < 0 then
    iabs := -i
else
    iabs := i
end;

This function returns the absolute value of an integer.

Functions may not return record or file values. They may return string values but not other kinds of arrays.

Forward References

Forward references are needed in some versions of Pascal, but not in Alice. The need for forward references arises when subprogram A must call subprogram B, but B has not yet been defined in the source code. Because of Pascal's strong type-checking requirements, it is necessary to know the types of B's parameters when A calls B. In Alice this is no problem, because Alice knows the types of everything by the time you run your program. With other versions of Pascal, however, this information may not be available, so a forward reference is necessary.

A forward reference is a partial declaration of a subprogram that appears before the subprogram is used or defined. This declaration consists of the keyword function or procedure, followed by the name of the subprogram, followed by declarations for the subprogram's arguments, followed by the word forward. The forward reference appears in the subprogram declaration section of a program or subprogram. Once the reference has been made, other subprograms can call the referenced subprogram, even if the referenced subprogram isn't actually defined until much later.

Such complications are not necessary in Alice and Alice Pascal programs will run without any forward references. However, if you are using Alice to prepare a Pascal program that will eventually run under another version of Pascal, you may need to put forward references into your program. To do this, type ``for'' on any Declaration placeholder that comes before the subprogram is called. Alice will lay out the template

forward routine;

Fill in the name of the subprogram for which you need a forward reference. When you use the TEXT command to save the program in text form, Alice will automatically transform this forward declaration into a forward reference of the proper format.

Because Alice uses this technique for forward references, you may not use the forward reference format used by other versions of Pascal.

Note that this format is converted over to Alice's convention by APIN.

The Stack

Local variables (declared in the var section of a function or procedure) are stored in a block of memory called the stack. When the subprogram is called, space for these variables is allocated from the stack's memory. When the subprogram terminates, the space that its variables occupied is de-allocated, and freed for other subprograms to use. This means that local variables must be initialized every time the subprogram is called -- they do not retain their values from one invocation to the next, because other subprograms use the same space for their local variables. In fact, the local variables may not be stored in the same place from one invocation to the next. The variables are simply stored in the next available space on the stack, so where they are stored depends on what's already on the stack.

Because local storage is allocated in this dynamic way, subprograms may be called recursively. Each invocation of the function has its own copy of its local variables. Therefore, if a function has a variable v and calls itself recursively, each invocation will have its own separate v, and operations on one v will not affect any of the others.

In addition to local variables, the stack is used to store the information that is needed for a subprogram to return correctly to its caller. This is usually called the linkage information.

The amount of stack space provided by Alice Pascal is ample for most programs. However, you may run out of space if your subprograms use a great deal of local storage space, or if the level of recursion becomes too great. You can avoid this problem in several ways.

Rewrite your program so it doesn't use so many local variables.

Don't use local arrays. Large local arrays can take up a lot of stack space. If you need a local array, allocate it dynamically with the new function. This function is described in Chapter 8.

Increase the amount of stack space for your program. Do this with the s= option on the command line that invokes Alice.

Arrays may be passed to subprograms in the same way that simple values are passed. They may be passed as either value parameters or var parameters. If they are passed as value parameters, the entire contents of the array will be copied so that the subprogram has its own copy of the array. Naturally, this process makes for a fair amount of overhead: the time needed to copy the array, and the stack space needed to store the copy. For this reason, it is usually preferable to pass arrays as var parameters.

Passing Subprograms as Arguments

Functions and procedures may be passed as arguments to other subprograms. In the parameter declarations for a subprogram that receives a function or procedure argument, you must also declare the parameters for the function or procedure being passed. For example,

procedure proc(function f(x:real):real;
               i,j:integer);

starts off a declaration for a procedure named proc. The parameters for this procedure are a function and two integers. The function takes one real parameter and returns a real value.

When a subprogram is passed as an argument to another subprogram, the parameter lists must match exactly in both caller and callee. It is not enough for the parameters to be compatible.