These statements provide a means of defining ICL procedures and of controlling the flow of execution within them. There are four of them:
Unlike direct statements, they can only be used in a procedure and are not accepted in direct mode.
The IF statement is essentially the same as the block IF of Fortran. It has the following general form:
The expressions (which must give logical values) are evaluated in turn until one is found to be true; the following statements are then executed. If none of the expressions are true, the statements following ELSE are executed.
Every IF statement must begin with IF and end with END IF (or ENDIF). The ELSE IF (or ELSEIF) and ELSE clauses are optional, so the simplest IF statement would have the form:
The following example procedure illustrates the use of the IF statement, and shows how they may be nested inside each other:
The LOOP statement is used to execute repeatedly a group of statements. It has three different forms:
This form sets up an infinite loop. Fortunately, an additional statement (BREAK) can be used to terminate the loop; BREAK would normally appear inside an IF statement within the loop. For example:
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 an integer value. Using this form of the LOOP statement you can simplify the previous example as follows:
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 must be specified to get a loop which counts down from a high value to a lower value. For example:
will count down from 10 to 1. LOOP WHILE: The third form of LOOP statement specifies loops which terminate on a condition. It has the form:
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 you can write yet another version of the COUNT procedure:
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 you for an answer to a question and has to keep repeating the prompt until a valid answer is received:
LIST | List a procedure |
EDIT | Edit a procedure |
SET EDITOR | Change editor used by EDIT |
SAVE | Save a procedure |
LOAD | Accept commands from a saved procedure |
DELETE | Delete a procedure |
PROCS | List procedure names |
VARS | List procedure variables |
SET (NO)TRACE | Switch tracing of procedures on/off |
An ICL procedure is like a subroutine in Fortran. It allows you to write a sequence of ICL statements which can later be run with a single command. The procedure may have parameters which are used to pass values to the procedure and return values from it.
To define a procedure, type a PROC command which specifies the name of the procedure and the names of its parameters. ICL then returns a new prompt using the name of the procedure (rather than ICL>) to show that you are in the procedure entry phase of procedure 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. For example:
To run the procedure you have entered, use its name as an ICL command and add any parameter values required:
The LIST command lists a procedure on the terminal. Just type ‘LIST’, followed by the name of the procedure you want to list:
Typing in procedures directly is fine for very simple procedures, but for anything complex it is likely that some mistakes will be made. When this happens, it will be necessary to edit the procedure. Editing can be done from within ICL using standard editors. For example the command:
can 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. For example, to select EDT, type:
When editing a procedure there are two possible options:
It is possible to create procedures from scratch using the editors. However, it is recommended that procedures be typed in directly to start with. The advantage is that during direct entry, any syntactic errors will be detected immediately. Thus, if you mistype the PRINT line in the above example you get an error message as follows:
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 encountered the ’is’ string when a right parenthesis was expected. Following such an error message, you 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.
It is sometimes useful to have a statement executed directly while entering a procedure. When using the direct entry method, this can be done by prefixing the command with a ‘%’ character. For example:
would give you on-line help information you might need to complete the procedure. If the ‘%’ was omitted, the HELP command would be included as part of the procedure.
Procedures created by direct entry will only exist for the duration of an ICL session. If you 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 your SQUARE_ROOT procedure on disk you would use:
To load it again, probably in a subsequent ICL session, you would use the LOAD command.
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. However, with LOAD it is possible to specify an alternative directory if required:
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:
This saves them in a single file with the name SAVE.ICL. They may then be reloaded with the command:
The SAVE ALL command is rarely used, however, because it is executed automatically when you exit from ICL. This ensures that ICL procedures do not get accidentally lost because you forget to save them.
Procedures can be deleted with the DELETE command. Just follow the command name with the name of the procedure to be deleted:
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:
When you run the procedure FRED you get the value of the variable X within 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 you can use procedures freely without having to worry about any possible side effects on variables outside them.
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 VARS command lists all the variables of a procedure. It has one parameter, which is the name of the procedure. If the parameter is omitted, the outer level variables, i.e. those that are not part of any procedure, are listed. Thus, after the previous example you would get:
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 it finishes execution, and if the procedure is executed a second time they will retain their values from the first time through the procedure on entry to the procedure for the second time. PROCS: To find the names of all the current procedures, use the ‘PROCS’ command:
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 be issued either 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.
You have seen that the LOAD command ‘loads’ a procedure that had previously been stored in a file by a SAVE command. What
actually does is to read ICL commands from the file SQUARE_ROOT.ICL and obey them. In the case of SQUARE_ROOT, these commands define a procedure of the same name. However, there is nothing to stop us storing any ICL commands you like in a file of type .ICL, and then you can load and execute them using the LOAD command. The PROC statement is designed only for creating procedures, so for creating general .ICL files you must use a normal editor. These general .ICL files are called Command Files.
A common use of command files is to store a set of command definitions for a monolith. You have already seen an example of this in file KAPPA_DIR:KAPPA.PRC. When you startup the KAPPA package, this command:
is automatically executed, and this causes the command definitions it contains to become effective. An example of setting up your own command file is given in Section 11.8.
A subtlety to be aware of is the difference between storing commands within a procedure in a command file, and just storing the commands. For example, if you wanted to store the command definitions for your TESTx programs (see Chapter 11) in a procedure (given that these programs had all been put into a monolith called TEST), you could do this from within ICL as follows:
In a later ICL session you could load this procedure from the file TEST.ICL in which it had been stored:
This command would read the commands from TEST.ICL and obey them, and this would have the effect of defining the procedure TEST. However, the DEFINE commands within the procedure would not be executed until the procedure was executed. Thus, the commands TESTC etc would not be recognised until you had run the procedure as follows:
However, if you used your favourite editor to store the following ICL commands in the command file TEST.ICL (independently of any ICL session):
in your next ICL session you could execute these commands by typing:
The difference from the previous method is that the commands TESTC etc are now defined and can be used immediately without having to execute a procedure.
It is sometimes useful to run one or more ICL procedures as a batch job in VMS. It is quite easy to set this up by using a parameter with the ICL command to specify a file from which commands will be taken:
This form of the command is equivalent to typing ICL and then typing:
Note that a LOAD file may include direct commands as well as procedures. In order to create a Batch job, you must set up a file which contains all the procedures wanted, 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 your earlier example procedure:
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 to add the additional direct commands. Suppose this file is called TABLE.ICL, then to create a batch job, a command file is needed which could be called TABLE.COM and would contain the following:
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:
The /KEEP qualifier specifies that the output file for the batch job is to be kept. This file will appear as TABLE.LOG in your top level directory and will contain the output from the batch job. An /OUTPUT qualifier can be used to specify a different file name or directory for it.
SIGNAL | Signal an ICL exception |
Error conditions and other unexpected events are referred to as Exceptions. When such a condition is detected in direct mode, a message is output. For example, if you enter a statement which results in an error:
you get a message consisting of the name of the exception (SQUROONEG) and a description of the nature of the exception. A full list of ICL exceptions is given in Chapter 19.
If the error occurs within a procedure, the message contains a little more information. For example, if you use your square root procedure with an invalid value, you get the following messages:
If one procedure is called by another, the second procedure will also be listed in the error message. For example, if you run the following procedure:
you get:
It is often useful to be able to modify the default behaviour on an error condition. You may not want to output an error message and return to the ICL prompt, but rather to handle the condition in some other way. This can be done by writing an Exception Handler. Here is an example of an exception handler in the SQUARE_ROOT procedure:
Now running the TABLE procedure gives:
The exception handler has two effects. First, the code contained in the exception handler is executed when the exception occurs. Second, the procedure exits normally to its caller (in this case TABLE) rather than aborting execution completely and returning to the ICL prompt.
Exception handlers should be placed after the normal code, but before the END PROC statement. There may be any number of exception handlers in a procedure, each for a different exception. The exception handler begins with an EXCEPTION statement specifying the exception name, and finishes with an END EXCEPTION statement. Between these may be any ICL statements, including calls to other procedures.
An exception handler does not have to be in the procedure causing the exception, but could be in a procedure further up the chain of calls. In your example you could put an exception handler for SQUROONEG in TABLE rather than in SQUARE_ROOT:
giving:
Below is an example of a pair of procedures which use an exception handler for floating point overflow in order to locate the largest floating point number allowed on the system. Starting with a value of 1, this is multiplied by 10 repeatedly until floating point overflow occurs. The highest value found in this way is then multiplied by 1.1 repeatedly until overflow occurs, then by 1.01 etc:
One exception which is commonly encountered is that which results when a ctrl/C is entered on the terminal. This results in the exception CTRLC and may therefore be used to abort execution of a procedure and return ICL to direct mode. However, an exception handler for CTRLC may be added to a procedure to modify the behaviour when a ctrl/C is typed.
The exceptions described up to now have all been generated internally by the ICL system, or, in the case of CTRLC, initiated by the user. It is also possible for ICL procedures to generate exceptions which may be used to indicate error conditions. This is done by the SIGNAL command which has the form:
where name is the name of the exception, and text is the message text associated with the exception. The exception name may be any valid ICL identifier. Exceptions generated by SIGNAL work in exactly the same way as the standard exceptions listed in SG/5. An exception handler will be executed if one exists, otherwise an error message will be output and ICL will return to direct mode.
One use of the SIGNAL command is as a means of escaping from deeply nested loops. The BREAK statement can be used to exit from a single loop, but is not applicable if two or more loops are nested. In these cases, the following structure could be used:
where the exception handler again contains no statements, but simply exists to cause normal procedure exit, rather than an error message, when the exception is signalled.
If you frequently need to define commands to run particular programs, it is convenient to define them in an ICL login file which will be loaded automatically each time ICL is started up. An ICL login file works in exactly the same way as a DCL login file. ICL uses the logical name ICL_LOGIN to locate this file, so store the required definitions in a file called LOGIN.ICL in your top level directory, and put the following definition in your DCL LOGIN.COM file:
where DISK$USER:ABC needs to be replaced by the actual directory used. This command file will then be loaded automatically whenever you start up ICL and can include procedures, definitions of commands, or indeed any valid ICL command. Below is an example of an ICL login file which illustrates some of the facilities which may be used:
The definition of the EDIT command shown in the login file above is done using a Hidden procedure. Since EDIT is an ICL command to edit procedures, if you just used DEFSTRING to define EDIT as DCL EDIT, you would lose the ability to edit ICL procedures — the EDIT command would always edit VMS files. The procedure used to redefine EDIT gets around this by testing for the existence of a dot in the name of the file to be edited using the INDEX function. If a dot is present, it assumes that a VMS file is being edited and issues the command ‘$ EDIT (name)’. If no dot is present, it assumes that an ICL procedure is being edited and the command ‘#EDIT (name)’ is issued. The # character forces the internal definition of EDIT to be used, rather than the definition currently being defined.
The procedure is written as a hidden procedure, indicated by the word HIDDEN preceding PROC. A hidden procedure works in exactly the same way as a normal procedure, but it does not appear in the listing of procedures produced by a PROCS statement, nor can it be edited, deleted, or saved from within ICL. It is convenient to make all procedures in your login file hidden procedures so that they do not clutter your directory of procedures and cannot be deleted accidentally.
HELP | Display on-line documentation |
DEFHELP | Define the source of Help information |
ICL includes a HELP command which provides on-line documentation on ICL itself. Using the DEFHELP command it is possible to extend this facility to access information on the commands you have added. In order to do this, you need to create a help library in the normal format used by the VMS help system. This is described in the VAX/VMS documentation for the Librarian utility. You can then specify topics from this library which will be available using the ICL HELP command by using a command of the form:
This will cause a:
command to return the information on EDIT in help library LIBRARY.HLB, rather than in the standard ICL library.
This final section lists some examples of ICL procedures taken from the KAPPA manual (SUN/95). They should help you understand how to use the programming facilities of ICL correctly. Many of the commands used in the procedures are KAPPA applications. Unsharpmask: Suppose you have a series of commands to run on a number of files. You could create a procedure to perform all the stages of the processing, deleting the intermediate files that it creates.
If NUM is set to 10, the above procedure obtains the statistics of the images named REDX1, REDX2, …REDX10. The ICL variable FILE is in parentheses because its value is to be substituted into parameter NDF. There is a piece of syntax to note which often catches people out. Filenames passed via ICL variables, such as FILE in the above example, must be preceded by an @. Flatfield: Here is another example, which could be used to flat field a series of CCD frames. Instead of executing a specific number of files, you can enter an arbitrary sequence of NDFs. When processing is completed a !! is entered rather than an NDF name, and that exits the loop. Note the ˜ continuation character. (It’s not required but it’s included for pedagogical reasons.)