Structured Loops in CLLE (CLP For “Oldsters” :)

Structured Loops in CLLE (CLP For

For many years we RPG coders were constantly reminded (pestered, really) about structured coding techniques. We were harassed about avoiding goto’s (an RPG II programmer’s best friend), replacing them with structured blocks (if, else, end, do, etc. – but don’t go too deep!) – and to emphasize subroutines (later functions and procedures).

And while this indoctrination was going on – not one thing was done about the woeful lack of the same type of structure in CL programs!

(By the way, the documentation for all this new stuff is pretty much hit-and-miss. And, since much of it is not necessarily intuitive, some trial-and-error is necessary. But, heck, I like to do that anyway ? These new opcodes and functions are also release dependent, so no guarantees).

Do Loops

Instead of this:

LOOP: RCVF
MONMSG CPF0000 EXEC(GOTO EOF)
~~~ all your wonderful code ~~~
GOTO LOOP
EOF: ~~~ do some other cool thing ~~~

You can do this:

DCL &EOF *LGL

DOUNTIL COND(&EOF)
RCVF
MONMSG CPF0000 EXEC(CHGVAR &EOF ‘1’)
IF (*not &EOF) DO
~~~ here’s where you process file data ~~~
ENDDO
ENDDO

Or, you can do this (the same, but abbreviated):

DCL &EOF *LGL

DOUNTIL COND(&EOF)
RCVF
MONMSG CPF0000 EXEC(LEAVE)
~~~ here’s where you process file data ~~~
ENDDO

Or this:

DCL &EOF *LGL

RCVF
DOWHILE COND(*not &EOF)
~~~ do your fun file stuff here ~~~
RCVF
MONMSG CPF0000 EXEC(CHGVAR &EOF ‘1’)
ENDDO

I suppose the last example, or some variation of it, would be the “preferred” method for most purists. But, I’ve always liked the idea of “bailing” from a loop when it’s no longer useful. (GOTO logic using a structured construct). LEAVE (exit the loop) and ITERATE (“goto” the top of the loop) are both supported here.

Keep in mind the difference between a DOWHILE loop and DOUNTIL loop: DOWHILE checks the condition at the beginning of the loop, DOUNTIL checks the condition at the end of the loop. Therefore, the DOUNTIL loop will always execute at least once, regardless of the starting condition.

Also, the condition can be any valid, logical CLLE condition. So, DOUNTIL (&INDEX = 99) could be used for an indexed loop. But there is a better option for that…

For/Next Loops

Another great addition to the structured CL toolkit is the FOR loop. (Yay!).

In the old days, a numerically incremented loop in CL was goofy at best, and looked something like this.

DCL &INDEX *DEC 3

LOOP: CHGVAR &INDEX (&INDEX + 1)
~~~ some code that uses the &INDEX value ~~~
IF (&INDEX >= 10) GOTO FINISHED
GOTO LOOP
FINISHED: ~~~ code that does something after the loop is finished ~~~

Yuck. Of course, there were many, many different variations of this kind of loop. Sometimes there were so many variables and tags inside the loop, it was nearly impossible to figure out what was what.

Now, you could use DOUNTIL or DOWHILE, which might look like this:

DCL &INDEX *DEC 3

DOUNTIL (&INDEX = 10)
CHGVAR &INDEX (&INDEX + 1)
~~~ your code that handles something based on index ~~~
ENDDO

Now, I suppose IBM could have created a DO loop concept similar to what was available in RPG3 (i.e. – DO 10 &INDEX), but since RPGLE included the much improved FOR loop, they decided CLLE should also use that opcode (although they named it DOFOR).

Or, maybe DOFOR is the only option they considered. I suppose that’s the more likely scenario.

Here’s the same as the code blocks above using a DOFOR loop

DCL &INDEX *INT

DOFOR VAR(&INDEX) FROM(1) TO(10) BY(1)
~~~ your amazing code using the index value ~~~
ENDDO

At the beginning of the loop &INDEX starts at 1. At the end of the loop, &INDEX is incremented, and as long as the value does not exceed the maximum of 10, the loop is repeated. Once the value of &INDEX reaches 10, the loop ends. Simple. Elegant. Consistent.

It is also notable that the DOFOR command has very specific parms instead of a condition. Readability is improved and the programmer is prompted for valid logical constructs. The first 3 parms – VAR, FROM, and TO – can use variables to control the loop. Only the BY parameter is required to be hard-coded (and defaults to ‘1’).

Variables used in a DOFOR loop must be either *INT or *UINT. *DEC variables are not supported.

The above DOFOR statement could also be typed in as

DOFOR &INDEX 1 10 BY(1)

for those coders who hate prompting commands. Personally, I like the prompted version.

Hack Alert!

Disclaimer: I can’t emphasize this enough – this is a hack I discovered the you should USE AT YOUR OWN RISK. It is NOT portable and could be totally messed up when applying a new release to your system. With that said…

One of the issues I had when I first started using these structured DO loop commands – for whatever reason IBM did not include “proper” END constructs for them. It’s kind of like when the IF and DO structured constructs were originally added to RPG – they were all closed by a simple END. It wasn’t until later releases that the improved ENDIF and ENDDO were included.

So, when I started using DOWHILE in CL programs, I found it incredibly annoying that I had to close the loop with a simple ENDDO. It’s difficult enough to keep track of “IF DO”/ENDDO blocks as it is without adding yet another ENDDO to the mix.

Oddly enough – and I’m quite surprised this actually worked (most of my attempted hacks don’t) – it is possible to successfully duplicate the ENDDO command as ENDUNTIL, ENDWHILE, or ENDWHILE. A simple CRTDUPOBJ suffixes.

CRTDUPOBJ OBJ(ENDDO) FROMLIB(*LIBL) OBJTYPE(*CMD) TOLIB(QUSRSYS) NEWOBJ(ENDUNTIL)

The program defined for the ENDDO command, which is QSYS/QCLENDDO, probably indicates to the CL compiler what is supposed to be done while creating the program object. (I’m not sure exactly how the the function of the command is resolved, and I really don’t care – as long as it works ? ).

Note that I duplicated the commands into QUSRSYS. Hopefully, this will make it release-safe and independent. We’ll see.

So, if you want an ENDUNTIL or ENDWHILE or ENDFOR to make your CL programs more readable, and you don’t mind taking chances with hacks like this, it is entirely possible and very easy to do it. Adding the CRTDUPOBJ to a special post-release program, or even the system startup program, should make it release-safe.

I suppose you could go one step further and experiment with duplicating DOUNTIL as UNTIL, DOWHILE as WHILE, and DOFOR as FOR. Talk about a readable CLLE program. I may try it (but, I’m NOT suggesting or recommending that you do it ? ).

Again, please, only do this on systems you “own” or at least given real control over.

Wish List

One additional loop I would like to see is – in both RPGLE and CLLE – is the marvelous FOREACH loop that is available in PHP (and other languages). It is a wonderful way to easily process an array without worrying about what the beginning, current, and ending index values may be. Of course, it would require full array capabilities in CL, and I suppose would have to be enabled using null-capable arrays in both languages (probably a few cool new built in functions or BIFs).

FOREACH, if implemented in CL, would be a great way to handle lists and mixed lists passed into the program from a user defined command. But, that’s another story…

Until next time…

Permalink to Structured Loops in CLLE (CLP For “Oldsters” :)’


Category: CLP, Coding
Tags: ,

Leave a Reply

Wordpress SEO Plugin by SEOPressor