HANDBOOK / CASE STUDY / The Compiler /

# Translating Procedures

We have just described how a source program is translated into an internal representation. We now discuss how to translate the internal representation into a target program.

    150  'action' Translate(Program: DECL)
    151     'rule' Translate(P):
    152        SetCurrentNesting(0)
    153        Procedure(P)
    154        Emit

    155  'action' Procedure(Proc: DECL)
    156     'rule' Procedure(dcl(Ident, proc(Formals,Locals,Body), Pos)) :
    157        GetCurrentNesting(-> OuterLevel)
    158        SetCurrentNesting(OuterLevel+1)
    159        DeclareList(Formals, 3 -> SizeFormals)
    160        DeclareList(Locals, SizeFormals+3 -> SizeLocals)
    161        ENT(SizeLocals)
    162        Statement(Body)
    163        RET
    164        LocalProcedures(Locals)
    165        UndeclareList(Locals)
    166        UndeclareList(Formals)
    167        SetCurrentNesting(OuterLevel)

    168  'action' LocalProcedures(Decls: DECLLIST)
    169     'rule' LocalProcedures(decllist(Head, Tail)) :
    170        LocalProcedure(Head)
    171        LocalProcedures(Tail)
    172     'rule' LocalProcedures(nil)

    173  'action' LocalProcedure(DECL)
    174     'rule' LocalProcedure(dcl(Ident, proc(Fs, Ds, S), Pos)) :
    175        HasMeaning (Ident -> object(procobj(Start,_),_,_))
    176        LAB(Start)
    177        Procedure(dcl(Ident, proc(Fs, Ds, S), Pos))
    178     'rule' LocalProcedure(Decl) :

`Translate`

The predicate `Translate(Program)` is invoked in the root clause of the specification. The whole program is merely represented as a simple procedure without parameters. Hence, we can simply use `Procedure` to process the given abstract syntax tree. Before calling `Procedure`, we initialize the current nesting level of procedures (which is implemented as a global variable). After generating the code, we invoke `emit` to write it to the target file.

`Procedure`

The predicate `Procedure(Proc)` handles a single procedure declaration the abstract syntax of which is

       dcl(Ident, proc(Formals, Locals, Body), Pos)

The procedure has a name given by `Ident`; formal parameters and local declarations are given by `Formals` and `Locals`; the procedure body is given by `Body`.

First, the current procedure nesting is updated accordingly. It is reset to its old value at the end of the rule.

During the processing of `Body` and local procedures, the formal parameters and the local declarations must be accessible. Hence, they are made visible by processing `Formals` and `Locals` with the predicate `DeclareList`. Outside the given procedure, the formal parameters and local declarations cannot be used. Hence, they are made invisible again (`UndeclareList`) after processing the body and the local procedures.

`DeclareList` (to be discussed later) has additional parameters: `DeclareList(X, Loc -> Size)` indicates that the items in `X` are stored from location `Loc` and that they occupy `Size` units.

The stack frame of the procedure is organized as follows: 3 units for administrative data (static link, dynamic link, return address), `SizeFormal` units as to store parameters, and `SizeLocals` units to store local variables.

The target code of the procedure is given as follows:

       ENT(SizeLocals)
       Code of body
       RET
       Code of local procedure

The code for the body is generated by the predicate `Statement`, the code for the local procedures is generated by `LocalProcedures`.

`LocalProcedures`

`LocalProcedures(Decls)` invokes `LocalProcedure` for each element of the declaration list(`Decls`).

`LocalProcedure`

If `LocalProcedure(Decl)` is invoked with a procedure declaration, it processes this declaration. Otherwise the passed element is skipped. A procedure is processed by `Procedure` (in the case of a local procedure, we first emit a start label).

We now refine the processing of procedure bodies. We discuss statements, expressions, and designators.