This is a mirror of a wonderful article from RPG Cafe!
(Short URL: https://ibm.biz/rpgCafe_Freeform_Rpg_Tutorial)

Variables and procedures

This chapter introduces you to defining constants, variables and prototypes.

Defining constants, variables and prototypes

The Definition statement, often called the "D spec" is used to define constants, variables, prototypes, and "procedure interfaces". The statement begins with DCL-x where x is C, S, DS, PR, or PI, depending on the type of item being defined. This is followed by the name, and then one or more keywords, then a semicolon.

Most of the keywords can go in any order you like, but if you have a data-type keyword such as CHAR or PACKED, it must be the first keyword.

Define a constant

The constant is the simplest RPG definition. It just has one keyword, CONST, and it's optional to actually specify the keyword. 100 and CONST(100) mean exactly the same thing for a constant definition.

Paste the following code into a source member, then compile and run it.

       dcl-c MAX_ELEMS 100;
dcl-c default_city_name 'London';
dsply max_elems;
dsply default_city_name;
return;

Define a standalone field

TThe standalone field is another simple RPG definition. A standalone field is an ordinary scalar variable. It has a name, a definition type of 'S', and a type.

Paste the following code into a source member, then compile and run it.

       dcl-s num int(10); 
for num = 1 to 3;
dsply ('i = ' + %char(num));
endfor;
return;

The definition statement defines a field named "num". The INT keyword indicates that the field is an integer. The "10" indicates that it has 10 digits; this is a 4-byte integer. The "s" in DCL-S indicates that it is a standalone field.

Bonus features in this example:

  • The "for loop".
  • The %char built-in function, which converts the numeric value to a readable character form such as "-12.345".
  • The '+' operator acts on strings as well as numeric values.

Exercise 3-1

  1. Define a standalone field of type character with length 5. The data type keyword for character is CHAR.
  2. Code an assignment statement to set the character field to 'hello'.
  3. Display the character field.

Solution

Define a data structure

A data structure is defined with one statement the data structure itself, using DCL-DS. This is followed by one statement for each subfield. Subfield statements just start with the name of the subfield. The data structure usually ends with an END-DS statement. If you are defining the data structure like another data structure by using the LIKEDS keyword, or like a record format by using the LIKEREC keyword, then you don't code END-DS.

Paste the following code into a source member, then compile and run it.

Note: This example uses the INZ keyword, which provides an initialization for the subfield. You can also code the INZ keyword for a standalone field.

       dcl-ds info qualified;
name char(10) inz('Sam');
salary packed(9 : 2) inz(50000.25);
end-ds;

dcl-ds otherInfo likeds(info) inz(*likeds);

dsply (info.name + ' has a salary of' + %char(info.salary));
otherInfo.name = 'Joe';
otherInfo.salary += 10000;
dsply (otherInfo.name + ' has a salary of' + %char(otherInfo.salary));

return;

Bonus features in this example:

  • The QUALIFIED keyword, which means that the subfields of the data structure must be qualified by the data structure name, DS.SUBFIELD. Without the QUALIFIED keyword, subfields are referred to just by their name.
  • The LIKEDS keyword is used to define another data structure with the same subfields as the parent data structure. The LIKEDS data structure is automatically qualified.
  • The INZ(*LIKEDS) keyword is used to initialize the LIKEDS data structure the same as the parent.
  • The += operator works the same as it does in C and Javaâ„¢. It adds the value on the right-hand-side to the variable on the left-hand side.

Define an array

You can define an array of scalars or an array of data structures.

RPG supports only one dimension for arrays. Multiple-dimension arrays can be simulated by using data structure arrays with array subfields; instead of coding cell(i j k) you would code table(i).row(j).col(k).

The dimension of the array is specified by using the DIM keyword.

Array indexes are specified with parentheses.

Define a scalar array

Paste the following code into a source member, then compile and run it.

       dcl-s dates date(*iso) dim(3); 
dates(1) = %date(); // the current date
dates(2) = %date() + %days(1); // tomorrow
dates(3) = %date() + %years(1); // next year
dsply (%char(dates(1)) + ' '
+ %char(dates(2)) + ' '
+ %char(dates(3)));
return;

Bonus features in this example:

  • The date data type with the ISO format (yyyy-mm-dd).
  • The %date built-in function, which returns the current date when no parameter is specified. %date can also convert a character or numeric parameter to a "true date".

Define a data structure array

Note: This example requires a 7.2 compiler, and you might need a PTF. See http://ibm.biz/spring_2017_rpg_enhancements for PTF details.

Paste the following code into a source member, then compile and run it.

       dcl-ds families qualified dim(5);
address varchar(50);
numPeople uns(3);
dcl-ds people dim(8);
name varchar(25);
age packed(5);
end-ds;
end-ds;

dcl-s numFamilies uns(5) inz(0);
dcl-s i int(10);
dcl-s j int(10);

families(1).address = '10 Mockingbird Lane';
families(1).people(1).name = 'Alice';
families(1).people(1).age = 3;
families(1).people(2).name = 'Bill';
families(1).people(2).age = 15;
families(1).numPeople = 2;
numFamilies = 1;
for i = 1 to numFamilies;
dsply (families(i).address);
for j = 1 to families(i).numPeople;
dsply (families(i).people(j).name + ' is '
+ %char(families(i).people(j).age)
+ ' years old.');
endfor;
endfor;
return;

Bonus features in this example:

  • The "name" and "address" subfields are defined as type VARCHAR. This means that the string variable has varying-length; the length given in the definition is the maximum length for the variable. The storage for a varying-length string variable is prefixed by a 2 or 4 byte value that holds the current length of the data part of the variable. In the example, the assignment of "Alice" to the name subfield would set the current length to 5.
  • The nested "people" subfield of the "families" data structure is defined with the DCL-DS keyword, so it is both a subfield and a data structure.
  • The unsigned 5 and unsigned 3 data types. UNS(5) defines a 2-byte unsigned integer that can hold up to 5 digits. UNS(3) defines a 1-byte unsigned integer that can hold up to 3 digits.

Exercise 3-2

  1. Change the data type for NAME to CHAR instead of VARCHAR.
  2. Recompile and run the program.
  3. Explain why the output doesn't look the same as before.

Solution

Define a prototype

RPG prototypes describe how to call a program, procedure, or Java method.

The definition statement starts with DCL-PR. Similar to data stuctures, the DCL-PR statement is followed by parameter definitions, and then the prototype is ended with an END-PR statement. You always need the END-PR statement for a prototype.

The EXTPROC or EXTPGM keyword indicates whether it is calling a procedure or program and it also indicates exactly which procedure or program to call. (Calls to Java methods also use the EXTPROC keyword.)

If neither the EXTPROC nor EXTPGM keyword is coded, the EXTPROC keyword is assumed.

Example 1: Call a program

A common program to call is QCMDEXC. This program runs a system command.

       dcl-pr qcmdexc extpgm('QCMDEXC'); 
theCmd char(3000) const;
cmdLen packed(15 : 5) const;
dbcs char(3) const options(*nopass);
end-pr;
dcl-s cmd varchar(100);

cmd = 'DSPJOB OUTPUT(*PRINT)';
qcmdexc (cmd : %len(cmd));
qcmdexc ('WRKSPLF' : 7);
return;

Bonus features in this example:

  • The program name in the EXTPGM keyword is case-sensitive. The system would not be able to find the program if the RPG program specified 'QcmdExc' in the EXTPGM keyword.
  • The CONST keyword indicates that the called program does not modify the parameter. When CONST is specified, the passed parameter does not have to exactly match the type and length on the prototype. If the type and length don't match, the RPG compiler creates a temporary variable of the required type and pass that temporary to the called program. Coding CONST also allows literals and expressions to be passed as parameters.
  • The call that uses the prototype is coded with the parameters in parentheses. The parameter separator is a colon, not a comma, as is more usual in other languages.
  • The %len built-in function returns the current length of the varying-length variable "cmd".

Note: If the RPG prototype name is the same as the actual program name, you can just code EXTPGM with no parameter. The RPG compiler uppercases the prototype name to determine the actual program name. Here is an alternate version of the QCMDEXC prototype.

dcl-pr qcmdexc extpgm;
theCmd char(3000) const;
cmdLen packed(15 : 5) const;
dbcs char(3) const options(*nopass);
end-pr;

Example 2: Call a procedure

For this example, we call the C runtime printf() function to print a message to the standard output instead of to the external message queue.


/if defined(*CRTBNDRPG)
ctl-opt dftactgrp(*no) actgrp(*new);
/endif

ctl-opt option(*srcstmt);
dcl-s num int(10) inz(25);
print ('This message is much longer than the 52 ' + 'characters that DSPLY allows. ' + 'The value of variable "num" is ' + %char(num));
return;
dcl-proc print;
dcl-pi *n;
msg varchar(5000) const;
end-pi;
dcl-pr printf extproc(*dclcase);
template pointer value options(*string);
dummy int(10) value options(*nopass);
end-pr;
dcl-c NEWLINE x'15';
printf(msg + NEWLINE);
end-proc print;

Bonus features in this example:

  • Instead of calling printf() directly, this example has a subprocedure called print() that handles the call to printf(). Calling printf() directly is a bit awkward because it has to add the new-line character, so it's convenient to wrap it in our own procedure.
  • The print() procedure defines a constant NEWLINE with the hexadecimal value x'15'.
  • The EXTPROC keyword uses *DCLCASE. This means that the external procedure is "printf", the same as the RPG prototype, in the same case as the RPG prototype. If we wanted to, we could code EXTPROC('printf'); we would have to do this if we wanted to use some other name for the RPG prototype, such as print_to_stdout.
  • The prototype for printf() has an extra "dummy" parameter. This is required because the C prototype for printf() indicates that it takes a variable number of parameters. The RPG way to indicate that a procedure takes a variable number of parameters is to use the OPTIONS(*NOPASS) keyword. OPTIONS(*NOPASS) indicates that it is not necessary to pass that parameter.
  • The "template" parameter for printf() is defined as a pointer (the POINTER data type keyword). The parameter is passed by value (the VALUE keyword). The parameter is defined with the OPTIONS(*STRING) keyword, which allows you to pass a character string as the parameter. When a character string is coded as the parameter, the RPG compiler creates a temporary variable with a "null-terminated" version of the parameter. printf() assumes that the first parameter is null-terminated; the null-terminator is used by printf() to determine the length of the first parameter.
  • Conditional compile directives for the first control statement (CTL-OPT statement), /IF and /ENDIF. The DFTACTGRP and ACTGRP keywords are only allowed with CRTBNDRPG, these directives control whether those keywords are seen by the compiler. If the CRTBNDRPG command is used, "*CRTBNDRPG" is defined, and the two H specs between the /IF and the /ENDIF is used in the compile. If the CRTRPGMOD command is used, those two lines are not included in the compile.
  • The control statements have three keywords.
    1. DFTACTGRP(*NO): This keyword is required if the program makes bound calls.
    2. ACTGRP(*NEW): This keyword sets the activation group for the program.
    3. OPTION(*SRCSTMT). This keyword causes the compile listing to have the same statement numbers as the source file. Most RPG programmers use this keyword.
{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}
>