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

2.7.7 Local Symbols

Besides the grouping of commands, compound statements provide a mechanism for the definition of local symbols and the allocation of dynamic storage. Declaration statements already have been explained in a previous section. All data objects which can be created in T3X may also be declared locally inside of compound statements by placing their declarations at the beginning of the statement block. Any number of declarations will be accepted after the DO keyword.

The declaration statements themselves do not change in local contexts. Only the position inside of a statement block makes the declared symbols local to that block. The statement

DO VAR i; FOR (i=0, 10) p(i); END

for example, applies the procedure p to the sequence 0...9. The index variable is declared inside of the same compound statement as the FOR loop generating the sequence. The variable i does not exist before the compound statement is entered. It will be created automatically at the point of its declaration and it will cease to exist at the end of the block it has been declared in. Therefore, variables which are local to compound statements are sometimes also called automatic variables.

Atomic variables, vectors, structures, constants, and objects may all be declared locally. (Unlike BCPL, though, T3X does not support nested procedure definitions.) Storage for atomic variables and vectors is allocated when the associated symbols become valid and released when the symbols are destroyed.

To illustrate another application of local storage allocation, imagine the following situation:

P() DO
        VAR     big_V[LARGE_1];
        VAR     big_W[LARGE_2]; ! Too big

        task1(big_V);
        task2(big_W);
END

In this procedure, two tasks requiring large amounts of storage shall be run sequentially, but not enough memory for both arrays is available. One solution would be the creation of two procedures where each one creates local storage for only one of the tasks. Another one would be to share the vector, but both solutions only work at the cost of readablility and maintainability. T3X provides another solution, since the compiler guarantees that local storage is allocated exactly at the point of its declaration and released immediately at the point of the destruction of its associated symbol:

P() DO
        DO VAR big_V[LARGE_1];
                task1(big_V);
        END ! big_V gets released here

        DO VAR big_W[LARGE_2];
                task2(big_W);
        END ! big_W gets released here
END

Since compound statements may be nested, naming conflicts may occur in many languages, like the following example (in C) illustrates:

{ int i;
        i = 123;
        { int i;
                i = 456;
        }
        printf("%d\n", i);
}

The variable i, which is defined in the outer compound statement, is redefined in the inner block. Inside of the inner block, the variable i is assigned the value 456. Clearly, the assignment i = 123; in the outer block references the variable defined in the outer block, but which one is referenced in the inner scope? C - like most other procedural languages - resolves this ambiguity by always giving precedence to the innermost definition. Therefore, the example program fragment would print 123. When this method is used, the symbol i defined in the outer scope becomes inaccessible in the embedded scope.

This effect is called shadowing: The inner definition `shadows' the outer one which thereby becomes temporarily invisible to the compiler.

T3X uses more strict scoping rules than most other languages: Symbols generally may not be redefined in T3X programs. This also applies to global symbols (symbols which have been declared at the top level - outside of procedure definitions, classes, or statement blocks). This way, shadowing can never happen. The flexibility of local symbols remains, though, since names can be reused in subsequent scopes:

F(x,y) DO VAR i, j;
        ! ...
END

G(x,y) DO VAR i, j;     ! The names x,y,i,j are re-used
        ! ...
END

As shown in this example, symbol names may be reused in procedure definitions (for formal argument names) as well as in subsequent compound statements. Since the variables i and j will be destroyed at the end of the compound statement forming the body of F, they can be reused in G. The same is valid for the argument names x and y.

The following figure shows some local and global symbols and their scopes.

+++ VAR     GX, GY;
 |
+++ CLASS A()
 |          STRUCT C=R,G,B;                        +++
 |                                                  |
 |          P(x, y) DO                        +++  +++
 |                  VAR x1, x2;               +++   |
 |          END                               ---   |
 |  END                                            ---
 |
+++ P(x, y) DO VAR x1, y1;                    +++
 |                                             |
 |          STRUCT  PT=PX,PY;                 +++
 |                                             |
 |          DO VAR i, j;                 +++   |
 |                  DO VAR x2, y2;  +++   |    |
 |                  END             ---   |    |
 |                                        |    |
 |                  DO VAR x2, y2;  +++   |    |
 |                  END             ---   |    |
 |          END                          ---   |
 |                                             |
 |          DO CONST t=%1, f=~t;         +++   |
 |                  DO VAR x2, y2;  +++   |    |
 |                  END             ---   |    |
 |          END                          ---   |
 |  END                                       ---
Fig.1 Scopes (example)

Like all other symbols, the global variables GX and GY are valid from the point of their declaration, but unlike locally declared names, they remain existant up to the end of the program. Their scope is the entire program (beginning at the point of their declaration). The scopes of all symbols in the example are illustrated using vertical bars. Plus signs indicate the point where a symbol name becomes valid and its storage is allocated, and minus signs mark the point of its destruction.

Note: the names x2 and y2, which are used in different scopes, denote different variables. A value stored in x1 within the first scope, for example, cannot be retrieved in the second or the third scope from x1, because the name references different locations in different scopes. The variable which is created at the beginning of the first scope containing x1 is deleted at the end of this scope and the value stored in that variable is lost. Assignments to local variables only remain valid between matching +++ and --- indicators.

All symbols which are defined in a so-called class contexts (between the keywords CLASS and the matching scope terminator END) are only valid inside of this context. Both, the structure C and the procedure P defined in the class A are only valid inside of the class context of A. There exists no conflict between A.P (the method P of A and the procedure P which is defined at the top level. Class contexts will be discussed in detail in the section on the T3X object model later in this document.

Previous:
2.7.6 Compound Statements
TOC | Index | Back Next:
2.7.8 Empty Statements