This is part two in a three-part series on moving to Free-Format RPG IV. Last time I covered the basics. This week I'll review some of the details of why MOVE/MOVEL was not included in free format, as well as a few other differences in coding in free format versus traditional RPG IV.
The MOVE and MOVEL opcodes do not work in free format because, well, I'm not sure why they are not implemented, but they aren't. In many conventional situations, the MOVE opcode can be converted directly into a free-format EVALR (eval with right-adjust) opcode, and MOVEL can often be converted into an EVAL opcode. However, it isn't always that straightforward.
MOVEL and MOVE are really polymorphic operations, and were long before IT grad students were using that word in a sentence. It allows you to:
And this is just off the top of my head; but there are many more.
Let's illustrate how to migrate these capabilities to Free-Format RPG IV:
The EVAL opcode is an assignment statement. When the target of a MOVEL is completely replaced by the value in Factor 2, an EVAL opcode may often be used instead. If both Factor 2 and the Result are character data, then EVAL may certainly be used. If both Factor 2 and the Result are numeric, then EVAL may often be used; however, if Factor 2 contains a numeric value that has more digits than the target variable, EVAL may not be used. There is no work-around for this unless you use the TRUNCDEC function in the RPGLIB service program. RPGLIB is a licensed commercial add-on service program for RPG IV.
Normally the EVAL opcode is an assignment statement. This means the data on the right side of the equals sign completely replaces the target field.
The MOVE and MOVEL opcodes, on the other hand, have an integrated substring function. That is, they replace the number of characters in the Result field with the number of characters in Factor 2. The balance of the Result field is unchanged. For example, if you MOVEL a 2-position character field to a 5-position character field, positions 1 and 2 of the 5-position field are replaced with the content of the 2-position field, but positions 3 to 5 of the 5-position field are not modified.
To simulate this with an EVAL operation, the %SUBST built-in function needs to be incorporated into the statement. For example, the following two statements (on fixed and free format) are equivalent:
.....DName+++++++++++EUDS.......Length+TDc.Functions++++++++++++++++++++++++
D REGION S 2A Inz('12')
D PRODUCT S 10A Inz('XXBIKERACK')
.....CL0n01Factor1+++++++OpCode++++Factor2+++++++Result++++++++Len++DcR1R2R3
C MOVEL REGION PRODUCT
C/free
eval %subst(product: 1 : %len(region) ) = Region;
/end-free
In this example, MOVEL opcode moves the 2 characters of REGION to the left two positions of the PRODUCT field. To simulate this with EVAL, the %SUBST built-in function is used to isolate the first few positions of the PRODUCT field. Only those positions are modified by the EVAL assignment. The MOVE or EVAL operation, in this example, results in PRODUCT = '12BIKERACK'.
If %SUBST were not used with EVAL, then the contents of PRODUCT would be replaced with the contents of REGION. The target of an EVAL is cleared before the operation is performed.
If the value needs to be right justified in the target variable, use EVALR instead of EVAL.
If the Result field and Factor 2 are not the same data-type, then when migrating to EVAL, a %CHAR, %EDITC, %EDITW, or %DEC built-in function may be required to convert the value. To convert a numeric to character, the following would be equivalent Fixed and Free-format statements. Both return '00123' to the target field.
D ITEMNO S 5S 0 Inz(123)
D ITMNBR S 5A
.....CL0n01Factor1+++++++OpCode++++Factor2+++++++Result++++++++Len++DcR1R2R3
C MOVE ITEMNO ITMNBR
C/free
evalR itmnbr = %editC(itemno:'X');
/end-free
If no leading zeros are required, use %CHAR instead of %EDITC and it'll return '123' instead of '00123'.
Going the other direction, converting character to numeric is a similar process. The %DEC built-in function can used to convert from character to numeric.
D ITEMNO S 5S 0 Inz(123)
D ITMNBR S 5A Inz('56')
.....CL0n01Factor1+++++++OpCode++++Factor2+++++++Result++++++++Len++DcR1R2R3
C MOVE ITMNBR ITEMNO
C/free
eval itemno = %dec(itmnbr:5:0);
/end-free
The %DEC requires that you include the length and decimal positions of the target variable. If no decimal positions are being used (as in this example) an alternative to %DEC is the %INT built-in function. It has no length or decimal position parameters and converts integers (whole numbers) in character fields to numeric.
Dates are actually easier in free format than traditional syntax, mostly. The MOVE opcode is used to convert between Date and non-Date values in traditional syntax, and the %DATE, %CHAR and %DEC built-in functions in free-format syntax.
D SHPDAT S 8S 0 Inz(906152009)
D SHPDATE S D
D DUEDATE S D
.....CL0n01Factor1+++++++OpCode++++Factor2+++++++Result++++++++Len++DcR1R2R3
C *USA MOVE SHPDAT SHPDATE
C SHPDATE ADDDUR 30:*DAYS DUEDATE
C/free
eval dueDate = %date(orddat:*USA) + %Days(30);
/end-free
When converting a zoned or character field that contains a "date" specify the format of the data in the non-Date field on parameter 2 of the %DATE opcode. Note that %DAYS is used in place of the ADDDUR opcode. There is also %MONTHS and %YEARS available.
The following examples summarize the subtle differences between a MOVE/MOVEL and EVAL,
.....DName+++++++++++EUDS.......Length+TDc.Functions++++++++++++++++++++++++
D DATA S 2A Inz('XX')
D TARGET S 10A Inz('9876543210')
.....CL0n01Factor1+++++++OpCode++++Factor2+++++++Result++++++++Len++DcR1R2R3
C MOVEL DATA TARGET
** TARGET = 'XX76543210'
C MOVEL(P) DATA TARGET
** TARGET = 'XX '
C MOVE DATA TARGET
** TARGET = '98765432XX'
C MOVE(P) DATA TARGET
** TARGET = ' XX'
C/free
eval %subst(target: 1 : %len(data) ) = data;
eval target = data;
evalR %subst(target: %len(target) - (%len(data)-1)) = data;
evalR target = data;
/end-free
Note that the basic difference (for character to character copy) is that MOVE and MOVEL require the "P" (Pad) Operation Extender to clear the target before the move is performed, whereas EVAL and EVALR always clear the target. Consequently, to avoid clearing the target with EVAL, a %SUBST built-in function needs to be used, making it substantially more complex than "MOVE A to B".
Of course, if the source and target are the same lengths, then a simple "EVAL TARGET = DATA" is all that's needed in free format and that's about as easy as it gets.
Another area that is different in free format vs. fixed is looping. Two of the looping opcodes DOW and DOU were introduced in the extended RPG IV syntax (extended Factor 2 syntax). So RPG IV programmers began using DOW and DOU instead of DOWEQ and DOUEQ. In addition, the DO opcode continues to be used as it is a fundamentally simple instruction.
But when free-format syntax was introduced, the DOW and DOU opcodes made it into the new sytnax, but the DO opcode was left out. Instead, IBM introduced a replacement for the DO opcode, and named it "FOR". The FOR looping opcode does everything DO could accomplish and more. It could perform a loop a fixed number of times and automatically increment or decrement a user-supplied counter; whereas DO can only increment a counter. And unlike DO, the FOR opcode requires a user-supplied counter variable. Here's a simple DOW (do while) loop in free-format syntax.
C/free
setLL (custno) ordHist;
if %EQUAL();
reade (custno) histRec;
dow NOT %EOF(ordHist);
csv = '"' + %char(custno) + '",';
csv += '"' + %char(orddate:*USA) + '",';
csv += '"' + %trimR(custname) + '",';
csv += '"' + %char(orddate:*USA) + '",';
writeIFS(myFile : %trimR(csv): %len(csv));
reade (custno) histRec;
enddo;
/end-free
The DOW and DOU let you place virtually any expression or conditional expression to the right of the opcode code. Enclose the expression in parentheses when necessary or for style. For example:
C/free
dow (A > B and (AmtDue - AmtPaid) > 0);
...
enddo;
dow NOT (FKey = F3 or FKey = F12);
...
enddo;
enddo;
/end-free
But what about the DO opcode? Since it is not supported in free format you have two options: (1) Drop out of free format and insert a DO opcode in fixed format, then drop back into free format. Or (2) Replace your use of the DO opcode with the FOR opcode. I prefer option 2, but let's look at both, just for fun.
.....DName+++++++++++EUDS.......Length+TDc.Functions++++++++++++++++++++++++
D ARR S 1A Dim(30)
D X S 3P 0
.....CL0n01Factor1+++++++OpCode++++Factor2+++++++Result++++++++Len++DcR1R2R3
C DO 30 X
C MOVEL(P) X'00' arr(x)
C enddo
.....DName+++++++++++EUDS.......Length+TDc.Functions++++++++++++++++++++++++
D ARR S 1A Dim(30)
D I S 10I 0
C/free
for i = 1 to 30;
arr(i) = X'00';
endfor;
/end-free
If we loose some of our legacy fixed-format habits, and instead use free-format style, we can expand this and make it even better:
.....DName+++++++++++EUDS.......Length+TDc.Functions++++++++++++++++++++++++
D ARR S 1A Dim(30)
D I S 10I 0
C/free
for i = 1 to %elem(arr);
arr(i) = X'00';
endfor;
/end-free
In this example I've implemented the use of the %ELEM built-in function. This built-in function returns the number of declared elements for an array or data structure array. It comes in pretty handy in situations like this.
Okay, now let's look at an example that works, but isn't really practical--although it works, it's really just a goofy illustration:
.....DName+++++++++++EUDS.......Length+TDc.Functions++++++++++++++++++++++++
D ARR S 1A Dim(30)
D X S 3P 0
C/free
// My other free-format code goes here
/end-free
C DO 30 X
C/free
arr(x) = X'00';
enddo;
/end-free
What I've done here is dropped out of free format, issued the traditional fixed-format DO opcode, then entered free format to complete the body of the DO loop. Note that the ENDDO opcode is also in free format. This is perfectly legal syntax (sadly). But don't try this at home, please!
With the implementation of IBM i 6.1 (V6R1M0 of the operating system), RPG IV has a pretty full complement of tools to get you into free format right away. But in some cases, it is still a bit behind what was available with traditional syntax. This is probably the biggest barrier to entry into RPG IV free format. But each new release brings more features, and, sadly, many people do not upgrade to the new releases since Microsoft has brainwashed the IT culture into thinking new versions of operating systems are supposed to be buggy. That's only Microsoft operating systems, not typically OS/400, i5 OS, or now, IBM i.
However, while the move to free format is now pretty much available to everyone, where there are shortcomings, don't be afraid to drop back into traditional fixed-format syntax and accomplish something with it instead of avoiding free format altogether.
That's all for this week. Next time I'll wind up this three-part series with a look at RPG IV Free-Format Coding Practices. Some call it "style," but I'm a fan of the Bruce Lee view of "style." The best style is to have no style, but to adapt. But that's next time.
Follow Bob Cozzi on Twitter at: Twitter.com/bobcozzi
Bob Cozzi is the author of Subprocedures and Service Programson DVD. A 3-disc training series that gives RPG IV programmers an easy way to learn how to get started with RPG IV Subprocedures and of course Service Programs. It is available today at www.RPGWorld.com/DVD. Bob's website www.RPGWorld.com is also the place to download the source code he features here in RPG Coder and additional examples he has created over the decades. The RPGWorld.com website welcomes thousands of System i RPG IV developers from all over the world, who share ideas and help each other with technical issues.