Getting Started with IBM i RPG: Variables and procedures

Variables and Procedures in RPG IV

This chapter explores the fundamentals of defining constants, variables, and prototypes in RPG IV. These elements form the building blocks for structuring data and enabling modular code.

Defining Elements with D Specifications

In RPG IV, the Definition specification (commonly called a "D spec") is used to declare constants, variables, prototypes, and procedure interfaces. A D spec begins with DCL-x, where x is C for constants, S for standalone variables, DS for data structures, PR for prototypes, or PI for procedure interfaces. This is followed by the element's name, one or more keywords, and a semicolon to end the statement.

Keywords can generally appear in any order, but data-type keywords (e.g., CHAR or PACKED) must come first.

Defining Constants

Constants are the simplest declarations in RPG. Use DCL-C followed by the name and value. The CONST keyword is optional: dcl-c MAX_ELEMS 100; is equivalent to dcl-c MAX_ELEMS const(100);.

Create a new source member, paste the code below, compile it, and run it to display the constant values.

dcl-c MAX_ELEMS 100;
dcl-c default_city_name 'London';

dsply max_elems;
dsply default_city_name;

return;

Defining Standalone Fields

A standalone field (or scalar variable) is declared with DCL-S, followed by the name and data type. This creates a simple, single-value variable.

Example: Define an integer variable and use it in a loop.

dcl-s num int(10);

for num = 1 to 3;
  dsply ('i = ' + %char(num));
endfor;

return;

Here, num is a 4-byte integer (10 digits total). The S in DCL-S denotes a standalone field.

Bonus Features:

  • The FOR loop iterates from the start value to the end value.
  • %CHAR converts numeric values to strings (e.g., "-12.345").
  • The + operator concatenates strings alongside numeric operations.

Exercise:

  1. Declare a standalone character field of length 5 using the CHAR data type.
  2. Assign the value 'hello' to it.
  3. Display the field.

Your Exercise Answer should look something like this:

dcl-s myCharField char(5);

myCharField = 'hello';
dsply myCharField;

return;

Defining Data Structures

Data structures group related fields and are defined with DCL-DS followed by subfield declarations. End the structure with END-DS. For templates (using LIKEDS or LIKEREC), omit END-DS.

The INZ keyword initializes subfields. It works for standalone fields too.

Example: Define a qualified data structure and a linked copy.

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:

  • QUALIFIED requires referencing subfields as DS.SUBFIELD.
  • LIKEDS copies the structure of the parent DS and auto-qualifies it.
  • INZ(*LIKEDS) initializes the copy with the parent's values.
  • += adds the right-hand value to the left-hand variable (like in C or Java).

Defining Arrays

RPG IV supports one-dimensional arrays via the DIM keyword. Access elements with (index). Simulate multi-dimensional arrays using nested data structure arrays (e.g., table(i).row(j).col(k)).

Scalar Array Example

Declare an array of dates and manipulate them.

dcl-s dates date(*iso) dim(3);

dates(1) = %date(); // Current date
dates(2) = dates(1) + %days(1); // Tomorrow
dates(3) = dates(1) + %years(1); // Next year
dsply (%char(dates(1)) + ' ' + %char(dates(2)) + ' ' + %char(dates(3)));

return;

Bonus Features:

  • DATE(*ISO) formats dates as yyyy-mm-dd.
  • %DATE() returns the current date (no params) or converts strings/numerics to dates.

Data Structure Array Example

Define nested structures for families and people.

dcl-ds person qualified;
  name varchar(25);
  age packed(5);
end-ds;

dcl-ds families qualified dim(5);
  address varchar(50);
  numPeople uns(3);
  people likeds(person) dim(8);
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:

  • VARCHAR creates variable-length strings (max length specified; prefixed by 2/4 bytes for current length). E.g., assigning "Alice" sets length to 5.
  • LIKEDS on a subfield nests a data structure.
  • UNS(5) (2-byte unsigned, up to 5 digits) and UNS(3) (1-byte unsigned, up to 3 digits).

Exercise

  1. Change the NAME subfield in person from VARCHAR(25) to CHAR(25).
  2. Recompile and run.
  3. Explain the output difference.

Answer

CHAR fields have fixed length, so they include trailing blanks in concatenations. VARCHAR trims them automatically. We can trim the CHAR statements using the built in function %TRIM

Defining Prototypes

Prototypes (DCL-PR ... END-PR) define calls to external programs, procedures, or Java methods. Always end with END-PR. Use EXTPGM for programs or EXTPROC for procedures (default if unspecified). The keyword specifies the target name.

Example 1: Calling a Program (QCMDEXC)

QCMDEXC executes system commands.

dcl-pr qcmdexc extpgm('QCMDEXC');
  theCmd char(3000) const;
  cmdLne 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:

  • EXTPGM names are case-sensitive (e.g., 'QCMDEXC' must match exactly).
  • CONST prevents modification; allows literals/expressions and auto-temps for mismatches.
  • Parameters use : separator in parentheses.
  • %LEN returns the current length of varying strings.

Alternate (if prototype name matches program): Omit the name in EXTPGM—RPG uppercases it.

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

Example 2: Calling a Procedure (printf)

DSPLY limits output to 52 bytes and the external queue. Wrap C's printf in a subprocedure for better printing.

/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:

  • Wrap printf in print for easier use (adds newline automatically).
  • NEWLINE constant: Hex x'15'.
  • *DCLCASE matches external name case (or specify EXTPROC('printf')).
  • Dummy param with OPTIONS(*NOPASS) handles varargs.
  • POINTER VALUE OPTIONS(*STRING): Passes null-terminated string (RPG adds terminator).
  • /IF//ENDIF: Conditional compile for CRTBNDRPG (defines *CRTBNDRPG).
  • CTL-OPT keywords:
    1. DFTActGRP(*NO): Required for bound calls.
    2. ACTGRP(*NEW): Sets activation group.
    3. OPTION(*SRCSTMT): Matches listing statement numbers to source.
{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}
>