Chapter 3
ICL Procedures

 3.1 Direct Entry of Procedures
 3.2 Running a Procedure
 3.3 Listing Procedures
 3.4 Editing Procedures
 3.5 Direct Commands During Procedure Entry
 3.6 Saving and Loading Procedures
 3.7 Control Structures
  3.7.1 The IF structure
  3.7.2 The LOOP Structure
 3.8 Prompting for Procedure Parameters
 3.9 Variables in Procedures
 3.10 Tracing Procedure Execution
 3.11 Running ICL as a Batch Job

An ICL Procedure is essentially the equivalent of a Subroutine in FORTRAN. It allows us to write a sequence of ICL statements which can be run with a single command. The procedure may have a number of parameters which are used to pass values to the procedure, and return values from it.

3.1 Direct Entry of Procedures

To enter an ICL procedure we type a PROC command which specifies the name of the procedure, and the names of any of its parameters. ICL then returns a new prompt using the name of the procedure rather than ICL> to show that we are in procedure entry mode. The statements that make up the procedure are then entered, followed by an END PROC or ENDPROC to mark the end of the procedure.

      ICL> PROC SQUARE_ROOT X
      SQUARE_ROOT> {  An ICL procedure to print the square root of a number  }
      SQUARE_ROOT> PRINT The Square Root of (X) is (SQRT(X))
      SQUARE_ROOT> END PROC
      ICL>

The line beginning with { is a comment which will be ignored by ICL when executing the procedure. The closing } is not necessary but looks neater.

3.2 Running a Procedure

To run the procedure we have entered we use the command format described earlier, using the procedure name as the command, and adding any parameters required.

      ICL> SQUARE_ROOT (2)
      The Square Root of         2 is 1.414214

But we can also specify the parameter without parentheses:

      ICL> SQUARE_ROOT 2
      The Square Root of 2 is  1.414214

What has happened here is that instead of the numeric value 2, we have passed the string ’2’ as the parameter. However, the SQRT function requires a numeric argument, so converts this string to a number. Thus, the free approach to type conversion, means that in many cases the rules for command parameters described in the Section 2.7.2 can be relaxed.

      ICL> Y=3
      ICL> SQUARE_ROOT Y
      The Square Root of Y is  1.732051

In this case the parameter passed was the string ’Y’, but once again this was converted to a numeric value for the SQRT function.

3.3 Listing Procedures

The LIST command can be used to list a procedure on the terminal. Just type LIST followed by the name of the procedure you want to list.

      ICL> LIST SQUARE_ROOT
  
  
          PROC SQUARE_ROOT X
          {  An ICL procedure to print the square root of a number  }
          PRINT The Square Root of (X) is (SQRT(X))
          END PROC
  
      ICL>

To find the names of all the current procedures use the command PROCS.

      ICL> PROCS
  
      SQUARE_ROOT
  
      ICL>

3.4 Editing Procedures

Entering procedures directly is fine for very simple procedures, but for anything more complex it is likely that some mistakes will be made in entering the procedures. When this happens it will be necessary to edit the procedure. Editing is accomplished from within ICL using standard editors. For example the command

      ICL> EDIT SQUARE_ROOT

would be used to edit the SQUARE_ROOT procedure. By default the TPU editor is used. It is also possible to select the EDT or LSE editors using the SET EDITOR command. All these editors are described in DEC’s documentation.

When editing a procedure there are two possible options.

It is possible to enter procedures completely using the editors. However it is recommended that procedures be entered originally using direct entry. The advantage is that during direct entry, any errors will be detected immediately. Thus if we mistype the PRINT line in the above example we get the following error message:

      SQUARE_ROOT> PRINT The Square Root of (X is (SQRT(X))
      PRINT The Square Root of (X is (SQRT(X))
                                  ^
      Right parenthesis expected

The error message consists of the line in which the error was detected. A pointer which indicates where in the line ICL had got to when it found something was wrong, and a message indicating what was wrong. In this case it found the ’is’ string when a right parenthesis was expected.

Following such an error message we can use the command line editing facility to correct the line and reenter it. If the same error occurred during procedure entry using an editor, the error message would only be generated at the time of exit from the editing session, and it would be necessary to EDIT the procedure again to correct it.

3.5 Direct Commands During Procedure Entry

It is sometimes useful to have a command directly executed while entering a procedure. When using the direct entry method this can be done by prefixing the command with a % character. For example:

      ICL> PROC SQUARE_ROOT X
      SQUARE_ROOT> %HELP

would give us on-line help information we might need to complete the procedure. If the % was omitted the HELP command would be included as part of the procedure.

3.6 Saving and Loading Procedures

Procedures created in the above way will only exist for the duration of an ICL session. If we need to keep them longer they need to be saved in a disk file. This is achieved by means of the SAVE command. To save our SQUARE_ROOT procedure on disk we would use:

      ICL> SAVE SQUARE_ROOT

To load it again, probably in a subsequent ICL session we would use the LOAD command.

      ICL> LOAD SQUARE_ROOT

The SAVE command causes the procedure to be saved in a file with name SQUARE_ROOT.ICL in the current default directory. LOAD will load the procedure from the same file, also in the default directory. With LOAD however it is possible to specify an alternative directory if required:

      ICL> LOAD DISK$USER:[ABC]SQUARE_ROOT

If you have many procedures you may not want to save and load them all individually. It is possible to save all the current procedures using the command:

      ICL> SAVE ALL

This saves all the current procedures in a single file with the name SAVE.ICL. These procedures may then be reloaded by the command:

      ICL> LOAD SAVE

The SAVE ALL command is rarely used however, because this command is executed automatically whenever you exit from ICL. This ensures that ICL procedures do not get accidentally lost because you forget to save them.

3.7 Control Structures

The ICL control structures provide a means of controlling the flow of execution within a procedure. Unlike the statements we have met so far these can only be used within a procedure and are not accepted in direct mode. There are two types of control structure:

3.7.1 The IF structure

The IF structure is essentially the same as the block IF of FORTRAN. It has the following general form:

      IF expression
          statements
      ELSE IF expression
          statements
      ELSE IF expression
          statements
      ELSE
          statements
      END IF

The expressions (which must give logical values) are evaluated in turn until one is found to be true, and the following statements are then executed. If none of the expressions are true the statements following ELSE are executed.

Every IF structure must begin with IF and end with END IF (or ENDIF). The ELSE IF (ELSEIF) and ELSE clauses are optional, so the simplest IF structure would have the form:

      IF expression
          statements
      ENDIF

The following example illustrates the use of the IF structure, and shows how one IF structure may be nested within another.

      PROC QUADRATIC A,B,C
  
      {  A Procedure to find the roots of the quadratic equation  }
      {  A * X**2 + B * X + C = 0                                 }
  
        IF A=0 AND B=0
          PRINT The equation is degenerate
        ELSE IF A=0
          PRINT Single Root is (-C/B)
        ELSE IF C=0
          PRINT The roots are (-B/A) and 0
        ELSE
          RE = -B/(2*A)
          DISCRIMINANT = B*B - 4*A*C
          IM = SQRT(ABS(DISCRIMINANT)) / (2*A)
          IF DISCRIMINANT >= 0
            PRINT The Roots are (RE + IM) and (RE - IM)
          ELSE
            PRINT The Roots are complex
            PRINT (RE) +I* (IM) and
            PRINT (RE) -I* (IM)
          ENDIF
        ENDIF
      END PROC

3.7.2 The LOOP Structure

The LOOP structure is used to repeatedly execute a group of statements. It has three different forms, the simplest being as follows:

      LOOP
          statements
      END LOOP

This form sets up an infinite loop, but an additional statement, BREAK, may be used to terminate the loop. BREAK would, of course, normally have to be inside an IF structure.

      PROC COUNT
          {  A procedure to print the numbers from 1 to 10  }
          I = 1
          LOOP
              PRINT (I)
              I = I+1
              IF I > 10
                  BREAK
              ENDIF
          ENDLOOP
      ENDPROC

As it is frequently required to loop over a sequential range of numbers in this way, a special form of the LOOP statement is provided for this purpose. It has the following form:

      LOOP FOR variable = expression1 TO expression2 [ STEP expression3 ]
          statements
      END LOOP

This form is essentially equivalent to the DO loop in FORTRAN. The expressions specifying the range of values for the control variable are rounded to the nearest integer so that the variable always has integer values. Using this form of the LOOP statement we can simplify the previous example as follows:

      PROC COUNT
          {  A procedure to print the numbers from 1 to 10  }
          LOOP FOR I = 1 TO 10
              PRINT (I)
          ENDLOOP
      ENDPROC

Note that there is an optional STEP clause in the LOOP FOR statement. If this is not specified a STEP of 1 is assumed. The STEP clause can be used to specify a different value. A step of -1 must be specified to get a loop which counts down from a high value to a lower value. For example:

      LOOP FOR I = 10 TO 1 STEP -1

will count down from 10 to 1.

The third form of the LOOP structure allows the setting up of loops which terminate on any general condition. It has the form:

      LOOP WHILE expression
          statements
      END LOOP

The expression is evaluated each time round the loop, and if it has the logical value TRUE the statements which form the body of the loop are executed. If it has the value FALSE execution continues with the statement following END LOOP.

Using this form we can write yet another version of the COUNT procedure:

      PROC COUNT
          {  A procedure to print the numbers from 1 to 10  }
          I = 1
          LOOP WHILE I <= 10
              PRINT (I)
              I = I+1
          ENDLOOP
      ENDPROC

In the above case the LOOP WHILE form is more complicated than the LOOP FOR form. However LOOP WHILE can be used to express more general forms of loop where the termination condition is something derived inside the loop. An example is a program which prompts the user for an answer to a question (e.g. yes or no) and has to keep repeating the prompt until a valid answer is received.

      FINISHED = FALSE
      LOOP WHILE NOT FINISHED
          INPUT Enter YES or NO:  (ANSWER)
          FINISHED = ANSWER = ’YES’ OR ANSWER = ’NO’
      END LOOP

3.8 Prompting for Procedure Parameters

Normally a procedure will not be obeyed unless the required number of parameters is provided – the TOOFEWPARS exception is signalled. However, this check can be switched off using the NOCHECKPARS command. Then the procedure will be executed with the parameter being undefined. This fact can be used to write a procedure which will prompt for an undefined parameter, e.g.:

  PROC SQUARE_ROOT X
  { An ICL procedure to print the square root of a number,   }
  { prompting if the number is not given on the command line }
    IF UNDEFINED(X)
      INPUT ’Give the value of X: ’ (X)
    ENDIF
    PRINT The Square Root of (X) is (SQRT(X))
  END PROC

Note that only trailing parameters may be omitted

3.9 Variables in Procedures

Any variable used within a procedure is completely distinct from a variable of the same name used outside the procedure, or within a different procedure, as can be seen in the following example:

  ICL> X = 1
  ICL> PROC FRED
  FRED> X = 1.2345
  FRED> =X
  FRED> END PROC
  ICL> FRED
  1.2345
  ICL> =X
         1
  ICL>

When we run the procedure FRED we get the value of the variable X in the procedure. Then typing =X gives the value of X outside the procedure which has remained unchanged during execution of the procedure. This feature has the consequence that we can use procedures freely without having to worry about any possible side effects of the procedure on variables outside it.

The situation is exactly the same as that in FORTRAN where variables in a subroutine are local to the subroutine in which they are used. In FORTRAN the COMMON statement is provided for use in cases where it is required to extend the scope of a variable over more than one routine. ICL does not have a COMMON facility but does provide an alternative mechanism for accessing variables outside their scope using the command VARS and the function VARIABLE.

The command VARS is used to list all the variables of a procedure. It has one parameter, which is the name of the procedure. If the parameter is omitted, then the outer level variables, i.e. those that are not part of any procedure are listed. Thus in the previous example:

      ICL> VARS FRED
                       X  REAL     1.23450E+00
      ICL> VARS
                       X  INTEGER          1
      ICL>

VARIABLE is a function whose result is the value of a given variable in a given procedure:

      ICL> = VARIABLE(FRED,X)
      1.234500
      ICL>

and thus allows a variable belonging to a procedure to be accessed outside that procedure.

Note that the variables belonging to a procedure continue to exist after a procedure finishes execution, and if the procedure is executed a second time, they will retain their values from the first time through the procedure.

3.10 Tracing Procedure Execution

The commands SET TRACE and SET NOTRACE switch ICL in and out of trace mode. When in trace mode each statement executed will be listed on the terminal. Trace mode is very useful for debugging procedures. The commands can either be issued from direct mode to turn on tracing for the entire execution of a procedure, or inserted in the procedure itself, making it possible to trace just part of its execution.

3.11 Running ICL as a Batch Job

It is sometimes useful to run one or more ICL procedures as a batch job. It is quite easy to set this up using a parameter with the ICL command to specify a file from which commands will be taken.

      $ ICL filename

This form of the command is equivalent to typing ICL and then typing

      ICL> LOAD filename

Note, that as mentioned earlier, a LOAD file may include direct commands as well as procedures. In order to create a Batch job we must set up a file which contains all the procedures we want, a command (or commands) to run them and an EXIT command to terminate the job. Here is the file for a simple Batch job to print a table of Square roots using our earlier example procedure.

  
      PROC SQUARE_ROOT X
      {  An ICL procedure to print the square root of a number   }
      PRINT The Square Root of (X) is (SQRT(X))
      END PROC
  
      PROC TABLE
  
      { A procedure to print a table of square roots of numbers  }
      { from 1 to 100                                            }
  
        LOOP FOR I=1 TO 100
          SQUARE_ROOT (I)
        END LOOP
      END PROC
  
      {  Next the command to run this procedure  }
  
      TABLE
  
      {  And then an EXIT command to terminate the job  }
  
      EXIT
  

This file can be generated using the EDIT command from DCL. If the procedures have already been tested from ICL, it is convenient to use a SAVE ALL command (or exit from ICL) to save them, and then edit the SAVE.ICL file adding the additional direct commands. Supposing this file is called TABLE.ICL. To create a batch job we also need a command file, which we could call TABLE.COM which would contain the following:

  
      $ ICL TABLE
      $ EXIT
  

It might also need to contain a SET DEF command to set the appropriate directory, or a directory specification on the TABLE file name if it is not in the top level directory.

To submit the job to the batch queue the following command is used

  
      $ SUBMIT/KEEP TABLE
  

The /KEEP qualifier specifies that the output file for the batch job is to be kept. This file will appear as TABLE.LOG in the top level directory and will contain the output from the batch job. A /OUTPUT qualifier can be used to specify a different file name or directory for it.