modula-2 home

  Home  
  Tutorial  
  Win32 API  
  Reference  
  Projects  
 

 

Chapter 7 - Overall Program Construction


We have pretty well covered the topic of how to put all the parts together to build up a program. In this chapter we will go over the whole process in order to clear up any loose ends and have the entire process in one place. There is nothing magic about the way the various pieces fit together but the rules must be followed in order to build a usable program.

Load and display the program named OVERPROG.MOD for the first look at the overall structure of the Modula-2 program. It would be well for you to keep in mind that there is a major category that we have not even hinted at yet, the issue of modules. They will be covered in Part III of this tutorial, and although they are very important, you can begin writing meaningful programs before you even hear what modules are or how they are used.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
MODULE OverProg;    (* Overall program construction example *)

FROM Terminal2 IMPORT WriteString, WriteLn;

     PROCEDURE Proc1;
     BEGIN
       WriteString("Procedure 1");
       WriteLn;
     END Proc1;

     PROCEDURE Proc2;
          PROCEDURE Proc3;
          BEGIN
            WriteString("Procedure 3");
            WriteLn;
          END Proc3;

          PROCEDURE Proc4;
               PROCEDURE Proc5;
               BEGIN
                 WriteString("Procedure 5");
                 WriteLn;
               END Proc5;
          BEGIN
            WriteString("Procedure 4");
            WriteLn;
            Proc5;
            Proc3;
          END Proc4;
     BEGIN
       WriteString("Procedure 2");
       WriteLn;
       Proc3;
       Proc4;
     END Proc2;

BEGIN
  WriteString("Main Program");
  WriteLn;
  Proc2;
  Proc1;
END OverProg.

Nested procedures

The program on display contains several levels of nested procedures as an illustration for you. The main program has lines 1 through 35 as its declaration part, and lines 37 through 42 as its statement part. Since the procedure definitions actually define the procedures called for by the main program, they correctly belong in the declaration part of the program. Only two of the procedures are actually callable by the main program, "Proc1", and "Proc2". The procedure "Proc1" is a simple procedure, but "Proc2" has additional procedures in its declaration part.

Procedure "Proc2" contains a declaration part in lines 12 through 29, and a statement part in lines 30 through 35. Its declaration part contains two procedures, "Proc3" and "Proc4". The nesting is carried one step farther in "Proc4" which contains the procedure "Proc5" in its declaration part. Procedures can be nested to whatever level desired according to the definition of Modula-2.

Who can call who?

It is important for you to clearly understand which procedure can call which other procedures. A procedure can call any procedure on the same level as itself provided that both have the same parentage, or any procedure that is included in its own declaration part at the level of its own declaration part. For example, the main program can only call "Proc1", and "Proc2". The others are nested within "Proc2" and are not available to the main program. Likewise the statement part of "Proc2" can call "Proc1", because it is on the same level, and "Proc3" and "Proc4", because they are within its declaration part. The procedure "Proc5" can only be called by "Proc4", because no other procedure is at its level. Note that if another triple nesting were included in "Proc1", its third level procedure could not be called by "Proc5" because they would not have the same parentage.

Nested procedures can be very useful when you wish to use a procedure that you don't want any other part of the program to be able to access or even see. A private procedure can therefore be written with no concern that the name may clash with some other part of the program and cause undesirable effects.

The important thing to gain from this program is that nesting is possible and can be very useful, and the definition of a procedure is the same as that of the main program. This means that procedures can be nested within procedures in any way that aids in designing the program.

Compile and run this program and see if you understand the output from it.

Where do we place constants, types, and variables?

Load MOREPROG.MOD, for examples of where you can put the other definitions in the declaration part of the program and the procedures. This is a repeat of the last program with CONST, TYPE, and VAR declarations added in every place where it is legal to put them. This is done as an example to you of where they can be put, so no explanation of details will be given. Some time spent studying this program should aid you in understanding even better the overall program construction problem.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
MODULE MoreProg;   (* More program construction examples *)

FROM Terminal2 IMPORT WriteString, WriteLn;

CONST MainC = 27;
TYPE  MainT = ARRAY[3..7] OF CARDINAL;
VAR   MainV : MainT;

     PROCEDURE Proc1;
     CONST Proc1C = 33;
     TYPE  Proc1T = ARRAY[-23..-15] OF CHAR;
     VAR   Proc1V : MainT;
           Proc11 : Proc1T;
     BEGIN
       WriteString("Procedure 1");
       WriteLn;
     END Proc1;

     PROCEDURE Proc2;
     CONST Proc2C = 22;
     TYPE  Proc2T = ARRAY[3..5],[-4..0] OF BOOLEAN;
     VAR   Proc2V : MainT;
           Proc21 : Proc2T;
          PROCEDURE Proc3;
          CONST Proc3C = -234;
          TYPE  Proc3T = ARRAY[12..13] OF MainT;
          VAR   Proc3V : MainT;
                Proc31 : Proc2T;
                Proc32 : Proc3T;
          BEGIN
            WriteString("Procedure 3");
            WriteLn;
          END Proc3;

          PROCEDURE Proc4;
          CONST Proc4C = 111;
          TYPE  Proc4T = CARDINAL;
          VAR   Proc4V : MainT;
                Proc41 : Proc2T;
                Proc42 : Proc4T;
               PROCEDURE Proc5;
               CONST Proc5C = "A";
               TYPE  Proc5T = ARRAY[22..222] OF CHAR;
               VAR   Proc5V : MainT;
                     Proc51 : Proc2T;
                     Proc52 : Proc4T;
                     Proc53 : Proc5T;
               BEGIN
                 WriteString("Procedure 5");
                 WriteLn;
               END Proc5;
          BEGIN
            WriteString("Procedure 4");
            WriteLn;
            Proc5;
            Proc3;
          END Proc4;
     BEGIN
       WriteString("Procedure 2");
       WriteLn;
       Proc3;
       Proc4;
     END Proc2;

BEGIN
  WriteString("Main Program");
  WriteLn;
  Proc2;
  Proc1;
END MoreProg.

What about order of declarations?

Load the program LASTPROG.MOD for an example of how the various fields can be ordered in the declaration part of the program. Notice that there are 2 procedures, two CONST's, two TYPE's, and two VAR's defined, but they are defined in a seemingly random order. The order is random and was done only to illustrate to you that the order doesn't matter as long as everything is defined before it is used.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
MODULE LastProg;

FROM Terminal2 IMPORT WriteString, WriteLn;

   PROCEDURE Dummy;
   BEGIN
   END Dummy;

VAR Index : CARDINAL;
TYPE Stuff = ARRAY[34..55] OF CHAR;

   PROCEDURE Smart;
   BEGIN
   END Smart;

CONST Number = 120;
TYPE  TypeOf = ARRAY[1..Number] OF Stuff;
CONST Neater = -345;
VAR   Count23 : TypeOf;
      Counter : INTEGER;

BEGIN
   WriteString("This program does not do a lot.");
   WriteLn;
END LastProg.

In only one case does the order matter. The compiler is very picky about where the IMPORT list goes because the Modula-2 language definition requires it to be first. In addition, the EXPORT list must immediately follow the IMPORT list. We will cover both of these in detail later, for now simply remember that the order of all declarations can come in random order as long as they follow the IMPORT/EXPORT lists and come before the statement part of the program.

Programming exercises

  1. Using the program OVERPROG, add some calls to illegal places to see what messages the compiler displays.
  2. Using the program MOREPROG, add some illegal variable references to see what messages the compiler displays.

 

Next: Chapter 8. Input/Output