The free-format specifications are a contemporary feature to the RPG language. They provide a syntax that is relaxed compared to traditional RPG.
Free-format specifications allow RPG operations to be specified in positions 8 through 80 of the source line. Free format statements may extend across multiple lines, if necessary. All RPG statements must be terminated with a semicolon (;). The letter "C", normally specified in position 6 of RPG calculation statements, is not specified on free format statements. Positions 6 and 7 must be blank in free format syntax. Traditional comments (an asterisk in column 7) are not allowed in free format syntax.
Free-format operations must be specified between a /FREE and /END-FREE compiler directive. These directives must appear on a line by themselves. This allows the compiler to process all traditional RPG specifications normally, while also supporting free-format statements as needed.
Figure 12.3 illustrates an example of the free-format specifications. Note that the code is the same as in the example listed in Figure 12.2, but has been reformatted to the free-format syntax.
.....C/FREE++++++++++++++++++++++++++++++++++++++++++++++++++++ C/Free 0001 Chain CustNo CustRec; 0002 If %Found(); 0003 Visits += 1; 0004 Update CustRec %Fields(Visits); 0005 endif; C/End-Free
Line 1, in Figure 12.3 performs a CHAIN operation to the customer master file. Note the arrangements of the file name and key field. This illustrates how all operation codes are altered for free-format, that is the opcodes name appears first, followed by factor 1 (if necessary), followed by factor 2 (if necessary), and the result field (if necessary). Should any factor or the result field be unnecessary, it is omitted. In addition, the semi-colon must be specified at the end of all free-format RPG statements. This includes all operation codes as well as the ELSE, OTHER and ENDxx operations.
Operation code syntax on free-format specification is substantially different from traditional fixed-format syntax. The following diagram illustrates the difference in syntax between these two specifications.
Traditional Syntax | Factor 1 | OpCode | Factor 2 | Result |
Free format Syntax | OpCode | Factor 1 | Factor 2 | Result ; |
Free format syntax requires a terminating semicolon at the end of all free format RPG IV statements. If a semicolon is not specified, a compile error will occur. The semicolon is required at the end of all operation code statements.
.....CSRn01Factor1+++++++OpCode(ex)Factor2+++++++Result++++++++ C CustNo Chain CustRec ....../FREE++++++++++++++++++++++++++++++++++++++++++++++++++++ Chain CustNo CustRec;
Not all operation codes may be used in free-format. Most notably are the MOVEL and MOVE, and there is no direct replacement for them in free-format. Although the EVAL and EVALR operations do most of the function of MOVEL and MOVE respectively, they are not equivalent functions. Therefore a direct translation is not always available. If factor 2 and the result field of the MOVEL operation are the same datatype and length, then EVAL and MOVEL are equivalent. Likewise, if factor 2 and the result of the MOVE operation are the same length, then EVALR and MOVE are equivalent.
The primary difference between MOVEL and EVAL is that MOVEL is effectively a substring-like function. That is, if factor 2 is shorter than the result filed, the number of bytes replaced in the result field is equal to the length of factor 2. Whereas EVAL is an assignment operation, consequently EVAL replaces the contents of the target field. The MOVEL(P) operation using the pad operation extender is equivalent to the EVAL operation.
Contemporary RPG IV contains a number of syntaxes to perform simple math. Originally, the ADD, SUB, MULT, DIV, and SQRT operation codes were used to do arithmetic. Today, the EVAL operation can be used along with expressions to perform any mathematical formula. In addition, free-format syntax allows assignment statements (i.e., the EVAL operation) to be used with or without the EVAL operation code named. That is, the EVAL operation code name is optional on the free format specification. The following two statements are equivalent free-format syntax mathematical operations:
.....C/FREE++++++++++++++++++++++++++++++++++++++++++++++++++++ /Free 0001 Eval C = A + B; 0002 C = A + B; /End-free
There are several syntaxes that may be used to perform math. In fact, there are no less than eight methods that may be used to add one value to another giving a result. The eight RPG IV statements in figure 12.4 perform identical functions; they add X to Y giving X.
.....C/FREE++++++++++++++++++++++++++++++++++++++++++++++++++++ /Free 0005 Eval X = X + Y; 0006 Eval X += Y; 0007 X = X + Y; 0008 X += Y; /End-free
.....CSRn01Factor1+++++++OpCode(ex)Factor2+++++++Result++++++++ 0001 C X Add Y X 0002 C Add Y X 0003 C Eval X = X + Y 0004 C Eval X + = Y
The /FREE and /END-FREE statements are required to start and end free-format syntax and are not strictly counted as program statements. Lines 5 and 6 are the same as lines 7 and 8 respectively; note that the EVAL operation code has been omitted from lines 7 and 8.
Traditional comments in RPG appeared on lines that contain an asterisk in position 7 of the source line. Figure 12.5 illustrates traditional comments.
.....CSRn01Factor1+++++++OpCode(ex)Factor2+++++++Result++++++++ * Retrieve the customer record. If found, increment * the customer's visit counter and update the record. 0001 C CustNo Chain CustRec 0002 C if %Found 0003 C Eval Visits = Visits + 1 0004 C Update CustRec 0005 C endif
The first two lines of the source in Figure 12.5 are comments. The asterisk in position 7 causes the compiler to ignore the entire line.
With free-format RPG, an alternate comment syntax is provided that allows comments to be intermixed with the actual statements themselves.
In Figure 12.6 three comment statements are embedded within the source code. Note the double forward slash (//), that indicates the beginning of a comment. When // is detected, the compiler ignores the rest of the physical line on which the // appears. An example of combination statement and comment line appears on line 3 of Figure 12.6.
.....C/FREE++++++++++++++++++++++++++++++++++++++++++++++++++++ C/Free // Retrieve the customer record. 0001 Chain CustNo CustRec; 0002 If %Found(); 0003 Visits += 1; // Increment visits // Update the customer file 0004 Update CustRec %Fields(Visits); 0005 endif; C/End-Free
The alternate syntax for comments may be used within /FREE and /END-FREE statements and in place of traditional comments. Figure 12.7 illustrates using the alternate style comments with traditional RPG statements.
.....CSRn01Factor1+++++++OpCode(ex)Factor2+++++++Result++++++++ // Retrieve the customer recordd. 0001 C CustNo Chain CustRec 0002 C if %Found // If it is an existing customer // increment the Visits field 0003 C Eval Visits = Visits + 1 // then update the file 0004 C Update CustRec 0005 C endif
Note that the appearance of the // symbols may occur at any point on the line. Unlike free format syntax, however, when // comments are intermixed with traditional RPG calculation specifications, they must appear on a line by themselves.
Data structures and arrays have different syntax grammars when they are addressed by traditional RPG calculation specifications. The enhanced and free-format RPG syntax however supports a unified data structure and array syntax. This syntax is referred to as "data structures as arrays" or the simpler "array data structures." Figure 12.8 illustrates using data structures as arrays with the enhanced syntax.
.....DName+++++++++++EUDSFrom+++To+++++TDc.Functions+++++++++++++++++++ 0001 D myStruct DS Dim(30) QUALIFIED 0002 D ItemNo 5I 0 0003 D ItemPrice 7P 2 0004 D Desc 20A 0005 D Prices S Dim(30) Like(myStruct.Price) 0006 D i S 10I 0 0007 C for i = 1 to %elem(myStruct) 0008 C eval Prices(i) += myStruct(i).ItemPrice 0009 C endfor
Figure 12.9 illustrates the same source code rewritten using free-format syntax.
.....DName+++++++++++EUDSFrom+++To+++++TDc.Functions++++++++++++++ 0001 D myStruct DS Dim(30) QUALIFIED 0002 D ItemNo 5I 0 0003 D ItemPrice 7P 2 0004 D Desc 20A 0005 D Prices S Dim(30) Like(myStruct.Price) 0006 D i S 10I 0 /FREE 0007 For i = 1 to %elem(myStruct); 0008 Prices(i) += myStruct(i).ItemPrice; 0009 endfor; /END-FREE
In the examples in Figures 12.8 and 12.9 the data structure myStruct is a multiple occurrence data structure. In this syntax, however, these types of data structures are referred to as data structure arrays and may contain an array index in places the complex OCCURS operation. The DIM keyword (line 1) declares the number of elements for the data structure array named myStruct. When the DIM keyword is used, the data structure may be used within the RPG source member like an array. The SORTA and MOVEA operations may not be used with data structure arrays. In addition, only qualified data structures may be declared as an array. To declare a qualified data structure the QUALIFIED keyword is use. In addition, the LIKEDS or LIKEREC keywords may be used; with these keywords, the QUALIFIED keyword is implied.
To access individual elements of a data structure array or regular array, parentheses are used (see line 8 in Figure 12.8 or 12.9). Within the parentheses a literal, named constant, field name, or procedure that returns a numeric value or a mathematical expression may be specified.
Manipulating date, time, or timestamp variables, referred to collectively as "date" value in free-format syntax is somewhat different from traditional fixed-format calculations. The arithmetic date operators are replaced by the + and – symbols. In addition, the duration codes have been replaced in free-format with built-in functions of a similar name.
Table 12.1 summarizes the differences between traditional fixed-format calculation specification date manipulation and free-format syntax.
Fixed-format Operation | Free-format Syntax |
---|---|
ADDDUR and SUBDUR – Add or subtract a duration. (Add a period of time to a date.) | + or – symbols along with the %YEARS, %MONTHS, %DAYS, %HOURS, %MINUTES, %SECONDS, and %MSECONDS built-in functions. e.g., StartTime = StartTime + %Hours(HRSWRK) |
SUBDUR – Subtract two date values. (Find the difference between two dates.) | %DIFF built-in function. e.g., nHoursWorked = %DIFF(nClockIn : nClockOut : *HOURS) |
EXTRCT – Extract a component of a date. | %SUBDT built-in function. e.g., nDayofMonth = %SUBDT(InvDate : *DAYS) |
TEST – Checking a variable for a valid date value. | TEST opcode e.g., TEST(DE) *YMD ORDDTE // check non-date for date TEST(E) INVDATE // check date variable for valid date |
Duration codes | Whenever a duration is needed, one of the duration code built-in functions may be used, including: %YEARS, %MONTHS, %DAYS, %HOURS, %MINUTES, %SECONDS, and %MSECONDS built-in functions. |
MOVE – Move a date value to a non-date value, or move a non-date value to a date value. | The built-in functions %DATE, %TIME, and %TIMESTAMP may be used to copy a non-date value to a date field. The %CHAR built-in function may be used to copy a date, time or timestamp value to a non-date field. |
The bottom line is that date, time, and timestamp variables are manipulated with built-in functions when used in free-format syntax, and with operation codes when used with traditional syntax. Figure 12.10 illustrates the use of many of the date built-in functions.
.....DName+++++++++++EUDS.......Length+TDc.Functions++++++++++++++++++ D DueDate S D D DueDte S 6S 0 D InvDate S D INZ(D'2003-03-15') D Today S D Inz(*SYS) D nDayOfMonth S 5I 0 D nDays S 5I 0 /FREE 0001 Test(DE) *YMD DueDte; 0002 if NOT %Error; 0003 DueDate = %Date(DueDte : *YMD); 0004 nDayofMonth = %SUBDT(DueDate : *DAYS); 0005 endif; 0006 nDays = %DIFF(Today : DueDate : *DAYS); 0007 if (nDays > 30); 0008 DueDate = InvDate + %Month(1); 0009 DueDte = %Char(DueDate : *YMD); 0010 endif; /END-FREE
In Figure 12.10, free-format line 1 uses the TEST operation to check the non-date field named DUEDTE field for a valid date value. If the date is valid, the %DATE built-in function is used to convert the non-date value to the real date field named DUEDATE. Then the day of the month is extracted from the DUEDATE field (line 4) and assigned to the nDayofMonth field.
On line 6 the number of days between the TODAY and DUEDATE fields is calculated using the %DIFF built-in function. On line 7 the number of days is compared to 30, and if greater than 30, one month is added to the value in the INVDATE field and the result is assigned to the DUEDATE field..
On line 9, the 6-digit zoned numeric field named DUEDTE is assigned the date value currently stored in the DUEDATE field. The format of the date value in copied to DUEDTE is set to YMD (year, month, day) by the %CHAR built-in function.
The TIME operation code is traditionally used to retrieve the current system date, time, or timestamp value at runtime. TIME is not supported in free-format syntax. In place of the time operation, the %DATE, %TIME, and %TIMESTAMP built-in functions may be used.
Normally these built-in functions are used to convert a non-date, time or timestamp values into real date, time, or timestamp values. When used without a parameter, they return the current system date, time, or timestamp value.
On line 1 in Figure 12.11, the current system date and timestamp is assigned to the DTS field. On line 2 the current system time is assigned to the RIGHTNOW field. Then on lines 3, 4, and 5, the fields YESTERDAY, TODAY, and TOMORROW are assigned corresponding date values.
.....DName+++++++++++EUDS.......Length+TDc.Functions+++++++++ D Yesterday S D D Today S D D Tomorrow S D D RightNow S T D DTS S Z /FREE 0001 dts = %Timestamp(); 0002 rightNow = %Time(); 0003 yesterday = %Date() - %days(1); 0004 today = %Date(); 0005 Tomorrow = %Date() + %days(1); /End-Free
Figure 12.12 illustrates a traditional RPG IV routine to fill up a subfile with records from a database file.
.....FFileName++IFEASFRlen+LKeylnKFDevice+.Functions++++++++++++++ FDSPCUST CF E WORKSTN SFILE(CUSTLIST : RRN) FCUSTMAST IF E K DISK /COPY FKEYS .....C*Rn01..............OpCode(ex)Extended-factor2++++++++++ C EVAL *INLR = *ON C EXFMT PROMPT C if FKEY = F3 C RETURN C endif C CUSTNO SetLL CustMast C if %FOUND C Read CustRec C DOW NOT %EOF C Add 1 RRN C Write CUSTLIST C CUSTNO ReadE CustRec C enddo C endif
Listed in Figure 12.13 is the identical routine rewritten in RPG IV free-format syntax.
.....FFileName++IFEASFRlen+LKeylnKFDevice+.Functions++++++++++++++ FDSPCUST CF E WORKSTN SFILE(CUSTLIST : RRN) FCUSTMAST IF E K DISK /COPY FKEYS /FREE *INLR = *ON; Exfmt Prompt; If FKey = F3; return; endif; Setll CustNo CustMast; if %Found(); Read CustRec dow NOT %eof(); rrn += 1; Write CustList; ReadE CustNo CustRec; enddo; endif; /End-Free
The free-format syntax of RPG IV provides more room on each line for expressions. Programmer habits may or may not lead to more readable code when using either syntax. One disadvantage of the free-format syntax is that none of the RPG editors on the market today support prompting of free-format syntax statements. Most editors do, however, allow traditional syntax RPG to be translated to free-format syntax through a keyboard macro. None of these editors, however, provides the ability to convert free-format RPG syntax statements back into traditional syntax.
There are several basic issues that may arise as a result of using free-format syntax for the first time. The following list may be helpful to those who have never used a noncolumnar language.
Always include a semicolon at the end of every RPG IV free-format statements. This includes conditional statements as well as all other statements, such as assignments and looping. Never include a semicolon at the end of traditional syntax RPG IV statements.
Start all RPG IV free-format statement groups with the /FREE compiler directive and end them with an /END-FREE compiler directive.
Embedded SQL statements may not be embedded between /FREE and /END-FREE statements.
The CALLP and EVAL operation codes are optional when used with free-format syntax. If unspecified, they are implied.
Parentheses are optional when used on parameterless built-in functions and procedure calls. However, it is highly recommended to use empty parentheses in order to visually distinguish between variables and procedures.