You are on page 1of 12

Coding in RPG IV - Chapter 1: Hello World

This chapter will take you through the steps to create and run an RPG IV program. It assumes you are using one of two ways to do your coding: 1. 2. RD Power (or its earlier cousins RDi or WDSC) PDM and SEU through some type of 5250 emulator

Initial setup
If you are using RD Power:

create a connection to your IBM i create a library create a source file create a source member in the file called HELLO.RPGLE open the member in the editor If you are using PDM and SEU:

logon to a session on your IBM i create a library using the CRTLIB command create a source file using the CRTSRCPF command WRKMBRPDM specifying your library and source file create a source member HELLO in the file using F6 in WRKMBRPDM or using the ADDPFM command, giving it type RPGLE open the member in SEU by using the "2" option

Entering the RPG program statements



Type the following code. Be sure the first '/' goes in column 7. You can see the column numbers at the top of the editor screen. If you are using SEU, the editor will not show you columns 1-5 by default, so the first column you see will be column 6. /free dsply 'Hello World'; return;

Compile the program. If you are using RD Power, you can compile from the menu within the editor, or you can save your changes and compile by rightclicking on the member in the navigation. Either way, select the CRTBNDRPG command, and take all the defaults. o If you are using SEU, exit the editor using F3, and compile using option 14. You could also compile from the command line using the CRTBNDRPG command, and using F4 to prompt the command.

Call the program If you are using RD Power, at this point you need to logon to a session on the IBM i. (If you are using PDM, you are already logged on.) Use the CALL command to call the HELLO program, substituting the name of your library for "yourlibrary". ===> CALL yourlibrary/HELLO

It should put up a message saying DSPLY Hello World Just press ENTER to end the program.

Dealing with compiler errors

Edit your source again, and change it so instead of displaying 'Hello World', it tries to display a variable called "name". This will be an undefined variable and will generate a compiler error. /free dsply name; return;

Delete the old version of the program using the DLTPGM command. Compile the program again. It will fail with a severity 30 error. If you are using RD Power, click on the RNF7030 error in the Error List Window. It should highlight the line that tried to display the "name" variable. If you are using SEU, use the WRKSPLF command and then use F18 to get to the end of the list of spooled files. Use option 5 on the HELLO spooled file and use the B command to go to the end of the listing. Page up a bit until you find the list of the error messages, then take note of the error message with the highest severity (the RNF7030 message). Return to the top of the file and enter RNF7030 on the search line, then hit F16 to locate the error message in the listing. The error message will have a statement number (statement 2). Page back in the listing to find statement 2 (on the left hand side of the listing). It should be the line that tried to display the "name" variable. The error message indicates that NAME is not defined. To correct it, you'll have to add a definition for a variable called NAME. (Note that RPG IV is not case-sensitive for variable names, so variable "name" is the same as variable "NAME". The upper-case form of the name will appear in the listing in error messages and the cross reference.) Add the following definition statement to the beginning of your source member. Be sure the initial D goes in column 6, and be sure the other text on the line stays in the same columns it appears here. RPG is column-sensitive for most of its statements, so the code must start in the correct column. D name /free dsply name; return; s 10a inz('Jim')

Recompile the program and call it again. It should put up a message saying DSPLY Jim

Debugging
Compile your program again, specifying a debug view other than *NONE or *STMT. For example, specify DBGVIEW(*LIST). (But *ALL, *SOURCE, or *COPY would all work fine here.) Now, start the debugger. For now, I'll just mention the system debugger, but there is a more user-friendly debugger associated with RD Power, RDi or WDSC that you can investigate separately. Assuming your program is called QTEMP/MYPGM, do the following commands: ===> STRDBG QTEMP/MYPGM - at this point, just use F10 to set a breakpoint on the first executable statement - you could also use F6 to set a breakpoint on a particular statement ===> CALL QTEMP/MYPGM - when you get a breakpoint, step (F10) through the program - display variables (F11) along the way ===> ENDDBG

That's it!
From now on, I'll assume you know

how to edit and compile your code how to locate the error messages associated with a failed compile how to call a program how to debug a program

Coding in RPG IV - Chapter 2: General info about RPG


This chapter will introduce you to general information about RPG source code.

RPG source code


The RPG compiler only uses columns 6 - 80 of a source member. Columns 1 -5 and columns 81+ are used for comments. Most RPG statements are coded using fixed form "specifications": 1. 2. 3. 4. 5. 6. 7. Control specifications, also called Header specifications, also called "H specs". These contain general-purpose keywords that affect the entire module. File specifications, also called "F specs". These define the files to be used in the module. Definition specifications, also called "D specs". These define constants, variables and prototypes. Input specifications, also called "I specs". These define the input record layouts for your files. The RPG compiler generates I specs for externally-described input-capable files. Calculation specifications, also called "C specs". This is where you code the logic of your procedures. Output specifications, also called "O specs". These define the output record layouts for your files. The RPG compiler generates O specs for externally-described output-capable files. Procedure specifications, also called "P specs". These start and end subprocedures. Between the starting and ending P specs of a subprocedure, you can code F, D and C specs, in that order.

The specifications must appear in the order given above, but it is not necessary to code all the specifications. A module could contain just a single C spec. Most RPG specifications contain the specification type in column 6. The exception is free-form calculation specifications which are coded between /FREE and /END-FREE directives. Free-form calculations have blanks in column 6 and 7. The examples given in chapter 1 all used free-form calculations.

RPG cycle
RPG was originally created to handle files. The intention was that the program would read a record from the "primary file" and for each record, it would perform the calculations. When it reached the last record, it would close the file and end the program. To artificially create the "last record" situation, the program could set on the "Last Record" indicator, called *INLR. For programs that do not have a primary file, it is still necessary to stop the program from looping through the calculations. This can be done two ways: 1. 2. By using the RETURN operation. By setting on the Last Record indicator. *inlr = '1'; There are subtle differences in these two mechanisms that you will learn in later chapters.

Coding in RPG IV - Chapter 3: Variables and procedures


This chapter will introduce you to defining contants, variables and prototypes.

Defining constants, variables and prototypes


The Definition specification, usually called the "D spec", is one of the "fixed-form" RPG specifications. This means that the various parts of the definition have to go in specific columns. All definition specifications have a D in column 6. The entry for the name of the item starts in column 7 and is 15 bytes long. The name can start anywhere in this entry. If the name is too long to fit in the entry, a "continuation" line can be coded, allowing the name to extend to column 77. The name must then be immediately followed by an ellipsis (three dots "..."). There are a few other fixed-column entries for the D spec, and there is a keyword area at the end of the D spec.

Define a constant
The constant is the simplest RPG definition. It has a name, a definition type of 'C' and a value. Paste the following code into a source member, then compile and run it. D max_elems c 100 D default_city_name... D c 'London' /free dsply max_elems; dsply default_city_name; return;

Define a standalone field


The 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. D num s 10i 0 /free for num = 1 to 3; dsply ('i = ' + %char(num)); endfor; return; The D spec defines a field named "num". The "i" is placed in the "data type" entry, indicating that the field is an integer. The "10" indicates that it has 10 digits; this is a 4-byte integer. The "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. 2. 3.

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

Define a data structure


A data structure is defined with a D spec for the data structure itself, with a definition type of 'DS'. The DS spec is followed by a D spec for each subfield; the subfields have a blank in the definition type.

Paste the following code into a source member, then compile and run it. Note: This example uses INZ keyword which provides an initialization for the subfield. You can also code the INZ keyword for a standalone field. D info ds qualified D name 10a inz('Sam') D salary 9p 2 inz(50000.25) D otherInfo ds likeds(info) D inz(*likeds) /free 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 only supports 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 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. D dates s d datfmt(*iso) dim(3) /free dates(1) = %date(); // the 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 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 Paste the following code into a source member, then compile and run it. D person ds qualified D name 25a varying D age 5u 0 D families ds qualified dim(5) D address 50a varying

D D

numPeople 3u 0 people likeds(person) dim(8) /free 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; dsply (families(1).people(1).name + ' is ' + %char(families(1).people(1).age) + ' years old.'); return;

Bonus features in this example:

The "name" and "address" subfields are defined with the VARYING keyword. When a string variable is defined with this keyword, the 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 "people" subfield of the "families" data structure is defined with the LIKEDS keyword, so it is both a subfield and a data structure. The 5u and 3u data types. 5u defines a 2-byte unsigned integer that can hold up to 5 digits. 3u defines a 1-byte unsigned integer that can hold up to 3 digits.

Exercise 3-2 1. 2. 3. Remove the VARYING keyword from the "name" subfield. Recompile and run the program. Explain why the output doesn't look the same as before.

Define a prototype
RPG prototypes describe how to call a program, procedure or Java method. The definition type is "PR". 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. D qcmdexc pr extpgm('QCMDEXC') D theCmd 3000a const D cmdLen 15p 5 const D dbcs 3a const options(*nopass) D cmd s 100a varying /free 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 say 'QcmdExc' in the EXTPGM keyword. The CONST keyword indicates that the called program will 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 will create 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 using 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".

Example 2: Call a procedure So far in these examples, the DSPLY opcode has been used. DSPLY has many limitations, including a 52-byte maximum. DSPLY also prints to the external message queue which is not always desirable. For this example, we'll call the C runtime printf() function to print a message to the standard output instead of to the external message queue. /if defined(*CRTBNDRPG) H dftactgrp(*no) H actgrp(*NEW) /endif H bnddir('QC2LE') D print D msg D num pr s 5000a varying const 10i 0 inz(25)

/free print ('This message is much longer than the 52 ' + 'characters that DSPLY allows. ' + 'The value of variable "num" is ' + %char(num)); return; /end-free P D D D D D D print b print pi msg printf pr template dummy NEWLINE c /free printf(msg + NEWLINE); /end-free P print e Bonus features in this example:

5000a

varying const extproc('printf') * value options(*string) 10i 0 value options(*nopass) x'15'

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 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 * data type). 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 will create a temporary variable with a "null-terminated" version of the parameter. printf() assumes that the first parameter will be null-terminated; the null-terminator is used by printf() to determine the length of the first parameter. Conditional compile directives for the H spec, /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" will be defined, and the two H specs between the /IF and the /ENDIF will be used in the compile. If the CRTRPGMOD command is used, those two lines will not be included in the compile. The H spec has 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. BNDDIR('QC2LE'): This keyword tells the compiler to add the QC2LE binding directory to the binding step.

Solutions to exercises

Exercise 3-1 D myCharfield s 5a /free myCharField = 'hello'; dsply myCharField; return;

Exercise 3-2 Without the VARYING keyword, the variable contains trailing blanks which are used when the variable is concatenated with other text. Bonus question: What RPG built-in function could be used in the concatenation expression to produce the same output in the version that does not have the VARYING keyword?

Coding in RPG IV - Chapter 4: Introduction to files


This chapter will introduce you to using files.

The File specification


The File specification is one of the fixed-form RPG specifications, so all the various parts of the file definition have to go in specific columns. All File specifications start with an F in column 6. The name specified in column 7 is the name used for the file within the RPG module. This name cannot be more than 10 characters.

A simple example
Let's start with a little example where we will just read all the records of a file. First, let's get a file to read. Enter the following command on the command line. The command will produce a file QTEMP/RPGTESTF that lists the *FILE objects in QGPL whose names start with QRPG. ===> DSPOBJD OBJ(QGPL/QRPG*) OBJTYPE(*FILE) OUTPUT(*OUTFILE) OUTFILE(QTEMP/RPGTESTF) Now, compile and run the following RPG program. Specify DBGVIEW(*LIST) on the compile command. Press ENTER on each DSPLY that shows up. Frpgtestf ip e disk /free dsply ODOBNM; If you haven't seen the power of RPG before, that might seem quite amazing that such a small and apparently simple program can do so much. Try running it under debug. When you first see the debug view, it will look very different from your original code. You will see several RPG statements that were generated by the RPG compiler. These are "Input specifications", and they describe the input buffer of the RPGTEST file. There is one I spec for each field in the file. When you step through the program, you will notice that you only get a breakpoint on the ODOBNM I spec. That is because the RPG program didn't use any of the other fields, so the RPG compiler did an optimization to avoid loading the data for those other fields. You will also notice that you step to the I spec and DSPLY opcode twice (at least, it was twice on my system, once for QRPGLESRC and once for QRPGSRC). This RPG program uses the RPG cycle. (You may have noticed that there is no RETURN operation and no setting of *INLR in the program.) The RPGESTF file is defined as a primary file (the "P" in the "IP" part of the definition). The RPG compiler generates code to read each record of the primary file and perform the calculations once for each record. If you are wondering how the file got opened, by default, RPG opens all files during module initialization. This is called "implicit open". If you don't want a particular file to be opened implicitly, you can code the USROPN keyword for the file, and code an explicit OPEN operation.

Using externally-described files


The previous program defined RPGTESTF as an externally-described file (the "E" in the F spec). The RPG compiler "extracts" the file definition as part of the compile, so it knows the names and buffer positions of all the fields in the file.

Explicitly handling the file


Let's try another version of that program that doesn't use the RPG cycle, but instead uses explicit calculations to handle the file. close rpgtestf; 1. 2. First, change the "File designation" from Primary to Full procedural. Use the help (F1) in the editor to find the right letter to enter. Let's do everything explicitly, even opening the file: open rpgtestf; 1. Add the USROPN keyword to the F spec for the file, in the keyword area. 2. Add the OPEN operation at the beginning of calculations. Next, add a loop to read the file. read rpgtestf; dow not %eof; dsply ODOBNM; read rpgtestf; enddo;

3. 4. 5. 6. 7.

8. 9.

CLOSE the file. Try compiling the program at this point. It will fail to compile with a message saying that the compiler cannot tell how the program will end. Recall that earlier examples all had a RETURN operation. 10. Add a RETURN operation. Here is the final program: Frpgtestf if e /free open rpgtestf; read rpgtestf; dow not %eof; dsply ODOBNM; read rpgtestf; enddo; close rpgtestf; return;

disk

usropn

Implicit opens and closes


Most RPG programmers use explicit loops to read a file, but they allow the RPG compiler to implicitly open and close the file. Let's try that. 1. 2. First, remove the USROPN keyword from the F spec. Now, remove the OPEN and CLOSE operations.

The RPG compiler will implicitly open the file when you call your program. But what about closing the file? The RPG compiler does not always close files when a program ends by using the RETURN operation. It only closes files when it finds the "Last Record" indicator, *INLR, to be on. You can simply set *INLR on at some point before reaching the end of calculations, or you can set on *INLR and immediately return. Many RPG programmers set *INLR on as the very first calculation, as a visible clue that the calculations are only meant to be run once. Other RPG programmers set *INLR on at the end of calculations. Either way works fine to cause the calculations to end and to cause the file to be closed. Here is the final program. Frpgtestf if e /free *inlr = '1'; read rpgtestf; dow not %eof; disk

dsply ODOBNM; read rpgtestf; enddo;

Exercises related to implicit open and close


Exercise 4-1
Using the last version of the program as an example, remove the assignment to *INLR, and add a RETURN operation at the end of the calculations. Frpgtestf if e disk /free read rpgtestf; dow not %eof; dsply ODOBNM; read rpgtestf; enddo; return; Call the program twice. Why does the program only produce output the first time it is called?

Exercise 4-2
Using the version of the program with the USROPN keyword as an example, remove the CLOSE operation (you can just comment it out using //). Frpgtestf if e disk usropn /free open rpgtestf; read rpgtestf; dow not %eof; dsply ODOBNM; read rpgtestf; enddo; // close rpgtestf; return; Call the program twice. Why does the program get an error the second time it is called?

Solutions to exercises
Exercise 4-1 When the RETURN opcode is used, and *INLR is not on when the program returns, the RPG compiler does not implicitly close the file. The next time the program is called, the file is still open and still at end of file, so subsequent READ operations will not find any records and the loop will exit immediately. Bonus activity to correct the problem: Add a SETLL *START operation for the file, before the READ loop. That will set up the file so it is positioned at beginning of file, and the program will work the same both time. setll *start rpgtestf; Exercise 4-2 When the RETURN opcode is used, the RPG compiler does not implicitly close the file. The next time the program is called, the OPEN operation fails because the the file already open. Bonus activity to correct the problem: Add a check to see if the file is already open before using the OPEN opcode. if not %open(rpgtestf); open rpgtestf; endif;

Coding in RPG IV - Chapter 5: Display files

This chapter will introduce you to using display files, also called "workstation files".

First, let's create a display file


Paste the following source into a source member with the name RPGDSPF and the source type of DSPF. The file has one input field, NAME. A R GETNAME A 5 5'What is your name?' A NAME 25A I 5 25 A CHECK(LC) Compile the file. It will create a display file called RPGDSPF.

Show a screen
Compile and run the following program. Frpgdspf cf e workstn /free exfmt getname; dsply ('Your name is ' + name); *inlr = '1'; This program just shows the screen and reads the input value. Then it uses the DSPLY opcode to show the value that it got.

A more complex interaction


Change the display file source to add another record format. The new record format SHOWINFO has some input/output fields (identified by the B for "Both") and an output field. Recompile the source to get a new version of the file. A R GETNAME A 5 5'What is your name?' A NAME 25A I 5 25 A CHECK(LC) A R SHOWINFO CA03(03) CA05(05) A 5 5'Hello ' A NAME 25A B 5 12 A CHECK(LC) A CURTIME T O 9 2 A 9 15'Current time' A DSPATR(HI) A CONDITION 10A B 10 2 A CHECK(LC) A 10 15'Current condition' A 23 2'F3=Exit F5=Refresh' A COLOR(BLU) Compile and run the following program. When you get to the part where it shows you the current time, try changing the condition to something else, say "Raining". The next time you see the screen, it will show "Raining" - try changing it to something else, say "Warm", but this time press F5 instead of pressing Enter. It will show "Raining" again. You can also change the name in the same way. Frpgdspf cf e workstn D done s n /free exfmt getname; done = '0'; condition = 'Cloudy'; dow not done; curtime = %time(); exfmt showinfo; select; when *in05; // Refresh, handled automatically when *in03; // Exit *inlr = '1'; return; other; // Just show the screen again endsl; enddo;

What's going on in the RPG program?


Compile the program with the listing view DBGVIEW(*LIST) or DBGVIEW(*ALL), and run it under debug. Set a breakpoint on the EXFMT opcode in the loop, that works with the SHOWINFO record format. Run the program, and see what happens on the EXFMT opcode. 1. 2. 3. 4. 5. 6. 7. First, it goes to some generated Output specifications which set the fields into the output buffer from the RPG program variables. Then it shows the screen. Then, after you press ENTER, it goes to the generated Input specifications where it loads the RPG program variables from the input buffer. Then it finally goes to the RPG SELECT statement following the EXFMT. If you pressed F3, the *IN03 indicator was set on. (Due to the CA03(03) coding in the display file source.) Similarly, if you pressed F5, *IN05 was set on. If you pressed ENTER, no indicator was set on.