Coding Requirements for Writing EQUEL Programs
This section describes the coding requirements for writing EQUEL programs.
Comments Embedded in COBOL Output
Each EQUEL statement generates one comment and a few lines of COBOL code. You may find that the preprocessor translates 50 lines of EQUEL into 200 lines of COBOL. This may result in confusion about line numbers when you debug the original source code. To facilitate debugging, a comment corresponding to the original EQUEL source precedes each group of COBOL statements associated with a particular statement. (A comment precedes only executable EQUEL statements.) Each comment is one line long and describes the file name, line number, and type of statement in the original source file.
Embedding Statements In IF and PERFORM Blocks
The preprocessor can produce several COBOL statements for a single EQUEL statement. In most circumstances, you can simply nest the statements in the scope of a COBOL IF or PERFORM statement.
There are some EQUEL statements for which the preprocessor generates COBOL paragraphs and paragraph names. These statements are:
retrieve
display
formdata
unloadtable
submenu
These statements cannot be nested in the scope of a COBOL IF or PERFORM statement because of the paragraph names the preprocessor generates for them.
Another consequence of these generated paragraphs is that they can terminate the scope of a local COBOL paragraph, thus modifying the intended flow of control. For example, a paragraph generated by the preprocessor in a source paragraph can cause the program to return prematurely to the statement following the PERFORM statement that called the source paragraph. To ensure that control does not return prematurely, you must use the THROUGH clause in the PERFORM statement.
The following example demonstrates the use of PERFORM-THROUGH and an EXIT paragraph to force correct control flow:
UNIX:
DATA DIVISION.
WORKING-STORAGE SECTION.
## 01 ENAME PIC X(20).
## DECLARE
PROCEDURE DIVISION.
BEGIN.
* Initialization of program
* Note the THROUGH clause to ensure correct control
* flow.
PERFORM UNLOAD-TAB THROUGH END-UNLOAD.
* User code
UNLOAD-TAB.
* This paragraph includes a paragraph generated by the
* preprocessor
## UNLOADTABLE Empform Employee (ENAME = Lastname)
## {
## APPEND TO person (name = ENAME)
## }
* This paragraph-name and EXIT statement causes control
* to pass back to the caller's scope
END-UNLOAD.
EXIT.
VMS:
* This paragraph-name causes control to pass back to
* the callers scope
END-UNLOAD.
USER-PARAGRAPH.
*Program continues
COBOL Periods and EQUEL Statements
You can optionally follow an EQUEL statement with a COBOL separator period although the preprocessor never requires that a period follow an EQUEL statement. If the period is present at the end of an EQUEL statement, however, the last COBOL statement that the preprocessor generates for that statement also ends with a period. Therefore, you should follow the same guidelines for using the separator period in EQUEL statements as in COBOL statements. For instance, do not add a period at the end of an EQUEL statement occurring in the middle of the scope of a COBOL IF or PERFORM statement. If you include the separator period in such a case, you prematurely end the scope of the COBOL statement. Similarly, when an EQUEL statement is the last statement in the scope of a COBOL IF, you must follow it with a period (or, alternatively, an END-IF) to terminate the scope of the IF.
For example:
IF ERR-NO > 0 THEN
* Do not use a separating period in the middle of an IF
* statement.
## MESSAGE "You cannot update the database"
* Be sure to use a separating period at the end of
* an IF statement.
## SLEEP 2.
In the example above, the absence of the period after the first message statement causes the preprocessor to generate code without the separator period, thus preserving the scope of the IF statement. The period following the sleep statement causes the preprocessor to generate code with a final separator period, terminating the scope of the IF.
An EQUEL Statement that Does Not Generate Code
The declare cursor statement does not generate any COBOL code. Do not code this statement as the only statement in any COBOL construct that does not allow null statements. For example, coding a declare cursor statement as the only statement in a COBOL IF statement causes compiler errors:
IF USING-DATABASE=1 THEN
## DECLARE CURSOR empcsr FOR
## RETRIEVE (employee.ename)
ELSE
DISPLAY "You have not accessed the database".
The code generated by the preprocessor would be:
IF USING-DATABASE=1 THEN
ELSE
DISPLAY "You have not accessed the database".
which is an illegal use of the COBOL ELSE clause.
Efficient Code Generation
This section describes the COBOL code generated by the EQUEL/COBOL preprocessor.
COBOL Strings and EQUEL Strings
UNIX: COBOL stores string and character data in a machine-dependent data item. The EQUEL runtime routines are written in another language (C) that verifies lengths of strings by the location of a null (LOW-VALUE) byte. Consequently, COBOL strings must be converted to EQUEL runtime strings before the call to the runtime routine is made.
In some languages, EQUEL generates a nested function call that accepts as its argument the character data item and returns the address of the EQUEL null-terminated string. COBOL does not have nested function calls, and simulating this would require two expensive COBOL statements. EQUEL/COBOL knows the context of the statement and, in most cases, will
MOVE the COBOL string constant or data item in a known area that has already been null-terminated. This extra statement is cheaper than the nested function call of other languages, as it generates a single machine instruction. Even though your COBOL-generated code may look wordier and longer than other EQUEL-generated code, it is actually as efficient.
VMS: VAX/VMS COBOL stores string and character data in a machine-dependent descriptor. The EQUEL runtime routines are written in another language (C) that verifies lengths of strings by the location of a null (LOW-VALUE) byte. Consequently, COBOL strings must be converted to EQUEL runtime strings before the call to the runtime routine is made.
In some languages, EQUEL generates a nested function call that accepts as its argument the VAX string descriptor and returns the address of the EQUEL null-terminated string. COBOL does not have nested function calls, and simulating this would require two expensive COBOL statements. EQUEL/COBOL knows the context of the statement, and in most cases will
MOVE the COBOL string constant or data item in a known area that is already null terminated. This extra statement is cheaper than the nested function call of other languages, as it generates a single machine instruction. Even though your COBOL-generated code can look wordier and longer than other EQUEL-generated code, it is actually as efficient.
COBOL IF-THEN-ELSE Blocks
There are some statements that normally generate an IF-THEN-ELSE construct in other languages that instead generate IF-GOTO constructs in COBOL. The reason for this is that there is no way to ensure that no EQUEL-generated (or programmer-generated) period will appear in an IF block. Consequently, in order to allow any statement in this scope, EQUEL generates an IF-GOTO construct. The code generated by EQUEL for this construct is actually very similar to the code generated by any compiler for an IF-THEN-ELSE construct and as efficient.
COBOL Function Calls
COBOL supports function calls with the USING clause for UNIX or the GIVING clause for VMS. This allows a function to return a value into a declared data item. EQUEL generates many of these statements by assigning the return values into internally declared data items, and then checking the result of the function by checking the value of the data item. This is less efficient than other languages that check the return value of a function using its implicit value (stored in a register). The generated COBOL has the overhead of assigning the value to a data item. An EQUEL/COBOL generated function call that tests the result can look like:
UNIX:
lCALL "IIFUNC" USING IIRESULT
IF (IIRESULT = 0) THEN ...
VMS:
CALL "IIFUNC" GIVING IIRESULT
IF (IIRESULT = 0) THEN ...