COBOL Unbounded Loops A Diatribe On Their Omission From the COBOL Standard (and a Plea for Understanding) August 11, 2016 Frank Swarbrick Principal Analyst Mainframe Applications Development FirstBank Lakewood, CO USA Introduction The COBOL standard (as of 2014), and thus IBM Enterprise COBOL, do not support a natural syntax for an unbounded loop via the native looping mechanisms. Several other COBOL implementations have extended the language to provide this missing support. 1. Micro Focus and GnuCOBOL support a PERFORM FOREVER inline perform loop. 2. GnuCOBOL supports PERFORM UNTIL EXIT as well, with the same behavior. 3. Fujitsu COBOL supports PERFORM WITH NO LIMIT syntax with the same behavior. The Fujitsu Software NetCOBOL V11.0 User s Guide has this to say: WITH NO LIMIT must be specified for the PERFORM statement to prevent the system generating code to determine the at end condition. When PERFORM WITH NO LIMIT is specified, the group of statements is executed repeatedly until an explicit exit or transfer of control is encountered. When you use the NO LIMIT phrase you must include statements in the performed code to determine when the iteration should end, and to exit the repeated processing. If you do not specify a statement to exit the repeat processing, processing continues indefinitely (in an infinite loop). Obviously several COBOL vendors and their customers have recognized that the lack of this feature is detrimental to desired coding practices. Shouldn t IBM Enterprise COBOL also support this feature as an extension. And shouldn t the COBOL standards committee (if one still exists!) provide this as a standard feature within the language? (Though now that there are at least three different syntaxes the question would be how do decide which syntax should be standard!) I write this document because IBM has already rejected my RFE requesting that Enterprise COBOL support some method of defining an unbounded loop, with their reasons being because it is not in the standard (*) and there are alternative methods of performing this facility. (*) Not in the standard hasn t stopped them from implementing many useful IBM extensions to COBOL, with one of the most recent being unbounded tables.
A Brief History of Loops in COBOL Genesis In the beginning there was the simple loop provided with the use of the GO TO statement. MAIN SECTION. PERFORM STOP RUN. PROCESS-MY-FILE SECTION. READ-MY-FILE. GO TO MY-FILE-AT-END. PROCESS-MY-RECORD. DISPLAY MY-RECORD. NEXT-RECORD. GO TO READ-MY-FILE. MY-FILE-AT-END. EXIT. Exodus Next there was the recommendation against the use of GO TO (in general), as well as some recommendation against use of procedure division sections. MAIN. PERFORM STOP RUN. PERFORM READ-MY-FILE. PERFORM UNTIL MY-FILE-AT-END PERFORM READ-MY-FILE READ-MY-FILE. SET MY-FILE-AT-END TO TRUE END-READ. PROCESS-MY-RECORD. DISPLAY MY-RECORD. So, what has been gained? And what has been lost?
The Good: - No use of GO TO. - No use of procedure division sections. - Continued use of separate paragraphs for discrete functions. The Bad: - Two different places where READ-MY-FILE is invoked. o Slightly confusing (though so prevalent you get used to it). o Bad for optimization. The optimizer either in-lines the READ logic in to two different places, or it does not inline it at all. - The addition of a variable to keep track of the at end condition. Leviticus (Yeah, my clever titling falls apart a bit here ) As an option to the above one could do the following instead. MAIN. PERFORM STOP RUN. PERFORM UNTIL MY-FILE-AT-END PERFORM READ-MY-FILE IF NOT MY-FILE-AT-END END-IF READ-MY-FILE. SET MY-FILE-AT-END TO TRUE END-READ. PROCESS-MY-RECORD. DISPLAY MY-RECORD. The Good: - Back to a single place where the record is read, allowing the optimizer to place it inline inside the loop. The Bad: - Continued need for the variable to keep track of the at end condition. - Redundant checking of the at end condition. The New Testament As of the 2002 COBOL standard there is now support for enhanced EXIT statements. This includes, which allows for early termination of a PERFORM loop. How might we make use of this for the above type of processing? MAIN. PERFORM
STOP RUN. PERFORM UNTIL MY-FILE-AT-END PERFORM READ-MY-FILE IF MY-FILE-AT-END ELSE END-IF READ-MY-FILE. SET MY-FILE-AT-END TO TRUE END-READ. PROCESS-MY-RECORD. DISPLAY MY-RECORD. Hmm, now that is interesting. We now exit the loop as soon as we know that - we want to terminate the loop - we don t want to do the process record logic prior to terminating the loop. But we still have: - Continued need for the variable to keep track of the at end condition. - Redundant checking of the at end condition. And in fact, there now will never be a case where the natural termination of the loop is reached, because we have explicitly terminated it with the so that any time the PERFORM loop itself checks for the at-end condition it will never be true. - So what is the point of even checking it there? Well, we have to check something! The COBOL standard does not allow an alternative. What if there were another way? MAIN. PERFORM STOP RUN. PERFORM UNTIL EXIT NOT END-READ The New Testament (Revised Edition)
PROCESS-MY-RECORD. DISPLAY MY-RECORD. Or a slight variation for the PROCESS-MY-FILE paragraph: PERFORM UNTIL EXIT END-READ The Good: - Both of these eliminate the need to set an at end variable only to check its value almost immediately afterward. - There is no need to perform the same paragraph in two different places. - No need for checking the truthness of a value that (at that point) can never be true. - It is still obvious (to me, anyway) what the condition is where the unbounded loop terminates. The Bad (?): - The read from file logic is no longer in its own paragraph, but is part of the inline perform. Since the processing of the record (PROCESS-MY-RECORD) is still out of line if feel having the inline READ in order to allow the to do the is a reasonable compromise. If you don t want to do that, you can always just use explicit FILE STATUS checking, as in the following example. PERFORM UNTIL EXIT PERFORM READ-MY-FILE IF MY-FILE-AT-END END-IF READ-MY-FILE. EVALUATE TRUE WHEN MY-FILE-SUCCESSFUL WHEN MY-FILE-AT-END CONTINUE WHEN OTHER PERFORM ERROR-WITH-MY-FILE END-EVALUATE. You could also use a DECLARATIVE section to handle unexpected I/O errors, which eliminates the need for the entire EVALUATE within READ-MY-FILE, which in turn makes READ-MY-FILE a single statement, so why have it in its on paragraph in the first place?
Of course there are other things loops are used for besides native COBOL I/O. In many of these cases there is a natural status code field (SQLCODE, IMS PCB status field, etc.) so there is not the issue with creating one only to be able to terminate the loop. Even in those cases I still think it makes sense to use an unbounded loop. PROCESS-MY-CURSOR. PERFORM UNTIL EXIT PERFORM FETCH-NEXT-RECORD IF SQLCODE = +100 END-IF FETCH-NEXT-RECORD. EXEC SQL FETCH MY-CURSOR INTO :MY-RECORD END-EXEC. EVALUATE SQLCODE WHEN ZERO WHEN 100 CONTINUE WHEN OTHER PERFORM ERROR-WITH-MY-CURSOR END-EVALUATE. (Not a Book in the Bible.) Reality So, the whole point to this is that neither the COBOL standard nor IBM Enterprise COBOL support the direct syntax for an unbounded loop. There are alternatives, are there not? Why don t you just use one of those? I will explain. The obvious alternative is to define a field that has a constant value, and then have your look check to make sure that the value is never some other value. 01 DUMMY-DONE-FLAG PIC X VALUE LOW-VALUES. 88 DONE VALUE HIGH-VALUES. PERFORM UNTIL DONE END-READ Does this work? Indeed, it does work, with several (important, at least to me) caveats.
You need to make sure you specify a VALUE for the field in its declaration. If you did not specify, for example, LOW-VALUES then there is the slight possibility that the field may coincidentally have a value of HIGH-VALUES already in it. In that case your loop would terminate immediately, having never performed any of the code inside it. Not good! OK, let s say you always remember to specify the VALUE clause. All is well, right? Not in my opinion. I have tested with COBOL 5.2 and several different values for the OPTIMIZE compile option. OPTIMIZE(0) Even though DONE is never true, the compiler still generates code to check to see if DUMMY-DONE-FLAG = HIGH-VALUES. So that s one instruction wasted. OPTIMIZE(1) and OPTIMIZE(2) Ah ha! The optimizer is doing its duty. It knows that DUMMY-DONE-FLAG can never be HIGH-VALUES, so it optimizes away the test. That s great! Well, except for the fact that this causes it does its duty so well that it eliminates the code in such a manner that it ends up causing a warning such as the following: IGYCB7300-W The code from lines ###.1 in program 'XXXX' can never be executed and was therefore discarded. So now a perfectly perfect (!) program results in warning and a Return code 4 condition!
Sidebar Although I am sure there are some obscure ones, I know of no major programming language other than COBOL that does not support unbounded loops. for (;;) {} while (1) {} C, C++, Java do forever; [ ] end; PL/I and REXX do loop; [ ] end; PL/I while true do [ ] Pascal, Delphi while 1: [ ] Python loop { } Ruby Perl 6 while true {} Groovy do [ ] loop Visual Basic while true {} Swift for { } Go LOOP [ ] END LOOP; Oracle PL/SQL and DB2 SQL PL WHILE 1=1 BEGIN [ ] END; Microsoft Transact-SQL Microsoft See the following link if you wish to know about more: https://www.rosettacode.org/wiki/loops/infinite. Interestingly, that page includes the supposed COBOL option of PERFORM UNTIL 1 <> 1, which is not valid COBOL at all in that at least one side of a comparison expression must not be a literal. (I guess it might be supported by some other COBOL implementation.) Not to mention that Enterprise COBOL does not even support <> as an abbreviation for not equal (though it is part of the 2014 COBOL standard). All of these, of course, have their own method of prematurely terminating a loop, just as COBOL now has (and CYCLE to iterate a loop).
Revelation All is not lost. While writing this document I thought of one more thing to try. And while I will not make any guarantees about it, it seems to do all of the following, at least with the version of Enterprise COBOL (5.2) that I am using: - Allows syntax that makes it obvious that this is an unbounded loop. - Allows the optimizer (both at OPT(1) and OPT(2)) to eliminate the code that checks for the termination of the loop. - It does so in a way that it does not cause the optimizer to issue any warning. At least in all the cases I have tested so far. The solution is to use the WITH TEST AFTER clause of the PERFORM, rather than the (default) WITH TEST BEFORE. (Hmm, just thought of PERFORM WITH NO TEST as another possible syntax for this loop construct!) For whatever reason (likely coincidence) this does all of the above, so let s go with it. I created a copybook named UNBOUND with the following lines: 01 PIC X VALUE LOW-VALUES. 88 UNBOUNDED VALUE HIGH-VALUES. REPLACE ==PERFORM UNBOUNDED== BY ==PERFORM WITH TEST AFTER UNTIL UNBOUNDED==. I chose to use UNBOUND/UNBOUNDED because it already exists as a contextsensitive keyword in Enterprise COBOL. That is, it is a keyword that can also be used as a user defined word. If you want to make it look more like Micro Focus COBOL s solution you can change UNBOUND and UNBOUNDED to FOREVER. Usage example: WORKING-STORAGE SECTION. COPY UNBOUND. PROCEDURE DIVISION. [...] PERFORM UNBOUNDED END-READ The REPLACE statement (assuming you don t have a REPLACE OFF before you get to it!) causes the final source code (as seen by the compiler) to be: PERFORM WITH TEST AFTER UNTIL UNBOUNDED END-READ
The optimizer still eliminates any checking of the unnamed filler that UNBOUNDED is a condition of to see if it s a value of high-values. But in this case it (apparently) leaves enough in place so that the optimizer hasn t eliminated every single instruction, and thus no superfluous warning is issued. So far now I think this is the best that can be done. In the end I still believe that IBM Enterprise COBOL, and the COBOL standard itself, should support at least one of these methods to represent an unbounded.