Refactoring RPG - Part 1 The Easy Stuff
by Stephen West


How Can RPG Be Refactored?

There are literally hundreds of books on refactoring java, but I am unaware of a single one for RPG. That leaves the details of refactoring RPG to us.

Our objective in refactoring RPG is to move the code forward by improving readability, maintainability, and removing deprecated opcodes and coding rules, without changing the external behavior of the code. Let's start with improving readability and maintainability. This can be as simple as replacing short, cryptic field names with longer, more descriptive names. For example, 'ACCTNBR' can be replaced by 'accountNumber'. Standardizing the appearance of the code will add to it's readability. A more complex form of refactoring would be to re-organize blocks of code into subprocedures and breaking up a large program into a series of smaller, distinct, reusable modules. Replacing deprecated opcodes and coding rules will keep the code fresh and will allow us to discontinue outdated coding practices.

To get started, let's focus on those refactoring tasks that we can automate. The more complex forms of refactoring RPG will have to wait for another day.

Deprecated RPGLE OpCodes

Try compiling a program with any of the following deprecated opcodes in free-format and you will get an error.

Deprecated OpcodeReplacement
ADDEVAL with + and +=
ADDDUREVAL with + or += combined with %years, %months, %days, and %diff
ALLOCEVAL with %alloc
BITOFFEVAL with %bitand and %bitnot
BITONEVAL with%bitor
CATEVAL with +
COMP=, <, <=, >, >=, and <>
DEFINEMove to Definition Specifications
DIVEVAL with /, /=, or %div
EXTRCTEVAL with %subdt
KFLD%kds or inline keys (key1:key2:etc...)
KLIST%kds or inline keys (key1:key2:etc...)
LOOKUP%lookup and %tlookup
MHHZOEVAL with %bitand and %bitor
MHLZOEVAL with %bitand and %bitor
MLHZOEVAL with %bitand and %bitor
MLLZOEVAL with %bitand and %bitor
MOVEEVAL and %char, %date, %dec, %dech, %graph, %int, %inth, %time, %timestamp, %ucs2, %uns, %unsh, or %editc
MOVELEVAL and %char, %date, %dec, %dech, %graph, %int, %inth, %time, %timestamp, %ucs2, %uns, %unsh, or %editc
MULTEVAL with * and *=
PARMList parameters in parenthesis, delimited using a colon (':'), following the name of the procedure to call
PLISTList parameters in parenthesis, delimited using a colon (':'), following the name of the procedure to call
SUBEVAL with - or -=
SUBDUREVAL with - or -= combined with %years, %months, %days, and %diff
TIME%date, %time, %timestamp
XFOOTEVAL with %xfoot
XLATEEVAL with %xlate

Deprecated RPGLE Coding Rules

Defining fields and data areas in the Calculation Specifications is still supported in RPGLE when using fixed-format, but not in free-format. Even if you are not currently coding for free-format, it would be a good habit to begin moving these declarations out of the Calculation Specifications and into the Definition Specifications.

We need some practical ideas for achieving our objective. Here is a [partial] list of things that could be done to refactor RPG:

  • Convert RPG III to RPG IV by using the IBM Convert RPG Source (CVTRPGSRC) command. RPG III has a source statement length of 80 bytes; RPG IV is 100 bytes. The REFACTOR command will pick up where the IBM provided CVTRPGSRC command leaves off. No sense in reinventing that wheel.
  • Clear out the first 5 positions of the RPG source. Some developers use this area to track program changes. Free-format RPG will compile with data in the first 5 positions, so clearing it out is not a requirement. You can choose not to use this refactoring item, or another option would be to use comments to track changes.
  • Blank out the form type column on comment lines. This is only for aesthetic purposes. We will count this towards standardizing code appearance.
  • Set all the source code lines to a specific date. Not a refactoring item, but may be useful.
  • Change the RPG opcodes to mixed case as specified in a file per programming standards. This feature is for implementing a coding standard. Standardizing the appearance of code will be part of our objective in refactoring RPG.
  • Change all the built-in functions (those beginning with %) to lower-case. Standardize code appearance.
  • Change all the figurative constants (*ON, *BLANK, etc...) to lower-case. Standardize code appearance.
  • Convert empty comment lines to blank lines by blanking out the asterisk in the comment line column. Standardize code appearance.
  • Remove any hexadecimal characters from the source code that are for the purpose of coloring and highlighting the source code. Highlighting source code makes it more visually appealing when viewed through SEU. However, it does nothing for the new generation of PC based source code editors.
  • Move field definitions from the calculation specs to the definition specs. Defining fields in the calculation specifications is not supported in free format RPG. They have got to go. Might as well be now.
  • Move data area definitions from the calculation specs to the definition specs. Defining data areas in the calculation specifications is not supported in free format RPG.
    For example, the code:
    	C     *DTAARA       DEFINE                  STCONTROL 

    	D STCONTROL       S                   DtaAra(STCONTROL)

  • Remove logical functions from conditional opcodes. This will shift from a proprietary representation to a conventional way of representing logical conditions. This improves the future portability of the code and overall readability (by non-RPG programmers).
    For example, the code:
    	C           KEY       IFEQ F03
    	C           KEY       OREQ F12  

    	C                   If        KEY = F03
    	C                             OR KEY = F12
  • Replace all variations of the MOVE opcodes with the EVAL opcode. The MOVE opcodes while very powerful are not supported in free format RPG. They can be replaced by the use of an EVAL opcode, but not easily. Any programmer attempting to manually code out the MOVE opcodes will spend a lot of time determining a suitable replacement. Automating this will save lots of time and help to reduce the risk of coding errors.
  • Replace arithmetic opcodes (ADD, SUB, MULT, DIV, Z-ADD, and Z-SUB) with the EVAL opcode. This at first glance appears easy since the fields are numeric right? There are subtle behaviors of Z-ADD that are not replaced with a simple EVAL. See 'Making Dollars and Cents of Free-Format RPGLE' in the December 2010 issue of SystemiNetwork magazine.
  • Replace *ENTRY PLIST with a prototyped procedure definition. The new method for defining the program entry point
  • Replace external program calls with the use of prototyped procedures to make external calls. The CALLP opcode is allowed, but no longer needed.
  • Get rid of PLIST's. This deprecated opcode is no longer needed when calling prototyped procedures.
  • Get rid of KLIST's. Also deprecated and replaced with the use of Key Data Structures or simply a list of key fields.
  • Convert from fixed-column to free-format. In order to make this change, all use of the deprecated opcodes must have been replaced. Field and data area declarations will need to have been moved from the Calculation Specifications to the Definition Specifications.

Maybe you can think of others, or maybe you would omit some of these from your list.

From here on we will be creating some programs (and a command) to automate some of these code refactoring ideas.

The Plan

This first of four article installments will implement the simple reformatting ideas, creating our base Refactor RPG tool. The tool will be coded in CMD, CLLE, and Free-Format RPG. Each installment I will keep adding to the Refactor RPG command until it includes most, if not all of the items listed above. Like bread crumbs left along a trail, each installment of this article will lead a programmer further along the path of building our own tool to automate [at least in part] the Refactoring of RPG.

I propose we start off with the simple stuff and progress to free-format. Here is 'The Plan':

Installment 1 (this one): Create a REFACTOR command to do the easy stuff.

Installment 2 (next one): Convert MOVE's and arithmetic opcodes (Z-ADD, Z-SUB, ADD, SUB, MULT, DIV) to EVAL's.

Installment 3: Change *ENTRY PLIST to a procedure interface and replace program calls with prototyped procedure calls.

Installment 4: Convert from fixed-format to free-format.

The Easy Stuff

The parameters of the REFACTOR command ( Figure 1 ) provide control over the refactoring items that are carried out. The default is to do them all. Set the ones you do not like to *NO. The source code for this command along with the source for all the objects needed to make the REFACTOR command work are here in the figures of this article. The source member to be refactored should already be RPGLE. If it is not, then you should use the IBM provided CVTRPGSRC command to convert if from RPG to RPGLE. One gotcha you should be aware of is that an RPGLE line is 20 characters wider than the same RPG line, so make sure to use a target source file that has a RCDLEN of at least 112.

Figure 6. Prompted REFACTOR Command

The CLLE command processing program for the REFACTOR command is CSC8801 ( Figure 2 ). There is nothing particularly magical here. A couple of file overrides, then it calls RPGLE program CS8801. That is where we will focus our attention.

Program CS8801 ( Figure 3 ) will loop through the source code to be refactored. As each line of code is processed (in the srFormatLine subroutine), a procedure will be called to implement each of our refactoring items. The program will decide if the source code line is to be changed and retained, or if it is to be discarded. You may be asking yourself why we would discard a line of code, so I will explain. Some programming techniques have been deprecated. For example, the data area definitions have been moved to the definition specifications, so the calculation specifications where data areas are defined can be discarded. Only lines of code that are no longer needed will be discarded.

Before looping through the code and doing the refactoring, some prep work needs to be done. In the spLoadRemaps subprocedure, we will read in the CSPREMAP file ( Figure 4 ) into a pair of arrays: FromVal and ToVal. These arrays are used later (in the spSetOpCode subprocedure) to convert the opcodes to the desired case (upper, lower, or mixed it's your choice). We will search the FromVal array for the opcode, and then replace it with the properly cased opcode in ToVal. The second file, CSPFIELDS ( Figure 5 ), eventually will contain information on all fields used in the source code to be refactored. To build CSPFIELDS, we will need to make a read only pass through the code to be refactored and write a record to CSPFIELDS for each data area definition or field definition that we find in the calculation specification. This information will be used to move the field and data area definitions out of the calculation specifications and into the definition specifications. Moving the fields up to the definition specifications is an always-on feature of the REFACTOR command, so it is not controlled by a parameter. In the next installment, this information will be expanded to include fields from all referenced files and it will be used for replacing MOVEs and arithmetic opcodes with EVAL's.

Admittedly, at this point, the REFACTOR tool is a bit sparse. However, each of the next three installments will add to it until it is able to accomplish much. The remainder of what you will need is in Figure 7 .

In closing the first installment, let me say this. The cataclysm for us programmers is not an asteroid, but the continual arrival of newer technologies. If we are to be like the mammals, and not the dinosaurs, we will need to continually adapt to the changing landscape ahead. Code refactoring is a well established software engineering tool that can assist us in keeping up with those changes. You would do well to embrace it. Now go forth my furry little friend, you have survived another article. Until next time!

Stephen West

© 2007 Workware, Inc. All rights reserved.