Midrange News for the IBM i Community


Posted by: Bob Cozzi
Rogue Programmer
Cozzi Productions, Inc.
Chicagoland
Bob's Blog
has no ratings.
Published: 03 Jun 2011
Revised: 06 Jul 2015 - 3189 days ago
Last viewed on: 28 Mar 2024 (8713 views) 

Using IBM i? Need to create Excel, CSV, HTML, JSON, PDF, SPOOL reports? Learn more about the fastest and least expensive tool for the job: SQL iQuery.

Bob Cozzi's top requirements for RPG IV 2011 Published by: Bob Cozzi on 03 Jun 2011 view comments(4)

RPG V - The Next Generation

A few years ago as IBM was working EGL and what is now called RDp, rumors started circulating about a new version of RPG. This new, fully free-format version would make it easier for IBM to add new features to the language while supporting 3rd-party add-ons improvements. This would mean you could install something like RPG xTools and the install routine would add-on RPG xTools' service programs so that they would be available implicitly to your code.

But the biggest change to an "RPG V" language is going totally free-format. RPG would finally become like C, C++, Java, JavaScript. PHP, Python, and other languages. Would that make it better? Probably not in and of itself, but with the columnar handcuffs off, RPG could certainly do whatever those smart IBM Canada folks can come up with.

To help things move along, I've composed a list of some of the changes to RPG along with a few improvements in the capabilities that I'd like to see in an "RPG V" implementation. There are in order of importance.

1. Comma as the Parameter Separator

How difficult is it to go from something like JavaScript or C, or C++ or EVERY OTHER LANGUAGE when coding something like this:

mysubproc( a, b, c);

To RPG IV and have to code this:

mysubproc( a: b: c);

The worse part is that the syntax checker doesn't catch the "error" when you use the commas. RPG requires colon separators because the old RPGII and RPGIII languages used the comma as a separator between an array name and an array index. To add a separator to those languages IBM had to select another character--they selected the colon.

Then when RPG IV was being designed, the dropped the comma notation for arrays and moved to a functional or parenthetical syntax:

arr,i  became arr(i)

But unfortunately they used the colon syntax as the parameter separator for functions and keywords. Since they dropped the array notation, the could have also dropped the colon notation in favor of a more traditional symbol--but you know how that story unfolds.

So my suggestion is when moving to an RPG V syntax, the number 1 priority should be to also migrate to comma-separator notation.

2. Free Format F and D Specifications

There is no reason to continue with fixed-format specs at any level. File Declaration Specifications and (data) Definition Specifications should be combined into a declarative operation/statement syntax. I've proposed this in the past, but basically it would be (dare I say it) as CL-like DCLF (for files) and DCL for everything else. Simple, flexible, expandable, and easy to remember.

    DCLF  CUSTMAST mode(U) Dev(DISK) EXTFILE('QGPL/CUSTMAST') TYPE(*DB2);
    DCLF  sales  mode(I) Dev(IFS) EXTFILE('/home/cozzi/sales.xml') TYPE(*XML);
    DCL   myArray Dim(25) Char(10);
    DCL   I  Int;
    DCL   bigInt  Int(8);
    DCL   smallInt Int(2);
    DCL   amountDue  Pkd(7,2) Inz(5.00);

The idea is that the keyword is the declaration identifier (file, data item, perhaps others), and the first parameter is always the item being declared. Then, following the item name is zero or more keywords that further specify the item's attributes. Proper default values would need to be adopted as well. For example to declare the CUSTMAST file as an input file using all the default values, I would simply code the following:

    DCLF  CUSTMAST;

The default would be a DB2 file opened for input (read-only). The file name would be the classic RPG style, where the CUSTMAST file on the library list would be opened; simple...

In addition the item defined by a DCLF would always be a file handle that could be passed as a parameter--eliminating the LIKEFILE syntax that so many people have difficulty comprehending.

Data Structures would easily migrate to free-format as well. We've seen how it can be done in CL--not perfect but good enough for CL, but for RPG V, I think we can do better.

    DCL  lineItem Struct Inz;
     DCL  item Char(5);
     DCL  desc varChar(50);
     DCL  price pkd(7,2);
     DCL  lineNo  int;
    END;
    DCL counter Int;
    DCL myItem Like(lineItem) Inz;

Unlike RPGIV, the LIKE keyword in RPG V would work for any type of data. We would not need LIKE, LIKEDS and LIKEFILE, just the LIKE keyword. They could also use REF or REFOBJ instead of LIKE and I'd be just as satisfied.

Note that the "END" keyword is used whenever an item is declared that is comprised of subitems. So in the case of Data Structures, Parameter Lists, or Object Classes, the END keyword would close off the group of related items. In the case where the LIKE keyword is used with a new data structure, the END keyword is not necessary but if you want to add additional items to a data structure or object class, then those declarations would require another keyword.

Certainly languages such as JavaScript, Java, C, and C++ that use the pair of curly brackets to denote begin/end have an advantage. But I think using this SQL-style naming convention for variable names, and extended it for data structures, classes (objects), and parameters (see below) makes it pretty viable.   

3. Migrate P Specifications, I and O Specs to Free-format D Specifications

Since File and Definition specifications will be consolidated, why not also eliminate the other 3 types of fixed-format specs?

The Procedure definition specification would easily move to the DCLx model described in item 2, above.

    DCL convert Proc EXPORT('Convert_Between_CCSIDs') ;
      DCL  rtnLength Int return value;
      DCL  inputValue Char(*) parm;
      DCL  inputLen   Int parm const;
      DCL  outputValue Char(*) parm;
      DCL  outputLen  Int parm const;
      DCL  rtnLength Int parm;
    DCL fromCCSID Int Inz(819);
    DCL toCCSID  Int Inz(37);

    DCL iconv  prototype('iconv'); // Prototype for iconv API.
      DCL rtnCode Int return value;
      DCL hConv Like(iconv_T) parm by Value;
      DCL input Ptr parm by ref;
      DCL inLen UINT parm by ref;
      DCL output Ptr parm by ref;
      DCL outLen UINT parm by ref;

Note that the parameter lists (for either the procedure implementation or the prototype) do not require an END keyword. This is because parameter list entries need to have the PARM keyword or in the case of the return value, the RETURN keyword "RETURN VALUE" is also allowed and means the same thing.

When defining a parameter, the default is to pass by reference. The "BY REF" keyword may be included to explicitly declare them as by reference, but it is the default. The "BY VALUE" keyword indicates that the parameter is passed by value (similar to the way C passes things).   The OPTIONS keyword is also supported and looks like it does in RPG IV, although there are new keywords that replace the old OPTIONS-style keyword. Here's an example:

     DCL  atoll prototype('atoll')
     DCL  rtnValue Int(8) return value;
     DCL  inputText String(TRIM) parm Const;

The C language runtime function of Character to Integer (ATOLL) which is technically named "ASCII to Long Long Integer" converts character text contain numbers into an integer. It is similar to using %INT except it doesn't blow up if the text is blank (it returns 0 instead of blowing up).

The prototype for it in the new RPG V would look like the above code. The return value is a 20I0 value, which is INT(8) in RPG V. The first and only parameter is a C-style null-terminated string. We also want to trim off any trailing blanks so the RPG IV equivalent of "OPTIONS(*TRIM:*STRING) is specified.

The STRING keyword means "pass this character parameter as a C-style null-terminated string". Under RPG IV, you'd have to declare the parameter as a pointer (*) and then pass the point "by value" and then include the OPTIONS(*STRING) keyword. Phew! That's a lot.  With RPG V, the data-type "string" means do all of this automatically, the inclusion of the TRIM option causes trailing blanks in the original value to be removed and the CONST keyword means its read-only so don't expect any changes to be returned on this parameter. The compiler is smart enough o figure out what you mean. 

The CONST keyword on the INPUTTEXT parameter means that the parameter is the C-style "const char* string" type. Without the CONST keyword, the parameter is passed as "char* string" that is a plain C-style null-terminated string.

Input and Output Specifications

Input and Output specifications would no longer exist. Everything would be declared in the Declaration specifications. Existing EXTERNALLY DESCRIBED Input or Output specifications could survive for compatibility and simplicity, but program-described data would only be permitted on DCL statements.

 Declaring an input record using DCL statements would be similar to declaring a Data Structure using DCL statements. There is some area for improved performance that could be implemented but for compatibility, IBM may choose to make things work the way they do today.

Output, on the other hand, hasn't really seen an improvement since the expanded the SPACE/SKIP "columns" to 3 positions some 15 year ago. If Output is moved to DCL statement, how would it look? Today with the Web Browser, PDF, MS Excel, XML and CSV being the major output targets for RPG code, I don't often think about what Printed Output on plain paper would look like.

Perhaps a DCL statement with a few output-oriented keywords would be introduced, SPACEB, SPACEA, SKIPB, SKIPA, etc. Someone else who is more in-tune with legacy PRINT FILES would probably need to talk to this issue.

4. Streamlined Subprocedure Naming

Take a look as this:

         EXPORT SYMBOL("RPGOPEN_convertCharacter")


.....D cvtChar         PR            10I 0 extProc('RPGOPEN_convertCharacter')                      
     D  input                         1A   OPTIONS(*VARSIZE)                                        
     D  inLen                        10I 0 Const                                                    
     D  output                        1A   OPTIONS(*VARSIZE)                                        
     D  outLen                       10I 0 Const                                                    
     D  fromCCSID                    10I 0 Const OPTIONS(*NOPASS)                                   
     D  toCCSID                      10I 0 Const OPTIONS(*NOPASS)                                   

.....P cvtChar         B                   EXPORT                      
.....D cvtChar         PI            10I 0                       

.....P cvtChar         E

No fewer than SIX locations contain the Subprocedure name; this is just nuts! Granted the PI line and the ending P spec don't technically require the proc name, it is strongly suggested for documentation purposes to include them. But look as this!

First we have the Binder Language EXPORT keyword. This keyword is typically stored in the QSRVSRC source file in a member with the same name as the service program containing the subprocedure. Here we identify the name of the subprocedure as it was exported. This is different from the name on the P specs and Procedure Interface line. In fact, there is no necessary connection between it and the name used in the source code.

That connection occurs on the Prototype. The EXTPROC keyword identifies the name that the subprocedure that matches the Prototype Name in columns 7 to 21. The name on the EXTPROC keyword is the Exported Name. The name by which the subprocedure is known. Note that this name does not match anything in the subprocedure body itself. Then there are the 4 instances of the Internal Name.

The Internal Name appears in columns 7 to 20 of the beginning and ending P specifications and optionally on the Procedure Interface (PI) statement. Now, RPG is a case-insensitive language, and this design is really the only practical solution. But it can be streamlined. Look as the same thing in RPG V:

    DCL cvtChar Proc EXPORT('RPGOPEN_convertCharacter');
      DCL  rtnLength Int return value;
      DCL  inputValue Char(*) parm;
      DCL  inputLen   Int parm const;
      DCL  outputValue Char(*) parm;
      DCL  outputLen  Int parm const;
      DCL  rtnLength Int parm;
      DCL fromCCSID Int Parm OPTIONAL;
      DCL toCCSID   Int Parm OPTIONAL;
        // body of subprocedure goes here.
    endproc;

No binder source needed, no prototype needed. The ENDPROC keyword (or simply "END" if IBM doesn't want to use ENDPROC) terminates the body of the Subprocedure implementation. The external name is identified by the EXPORT keyword itself. Binder source would still be needed when a Signature is generated for a service program.

There are additional keywords for the DCL PROC statement that control access to the subprocedure :

  • PUBLIC (the default when EXPORT is specified) - exports the subprocedure from the module and subsequently its *SRVPGM so other programs may call it.
  • PRIVATE - exports the subprocedure from the module but NOT from its *SRVPGM.
  • PROTECT(ED) - (the default when EXPORT is NOT specified) the subprocedure is private to the module in which it is implemented. It is not exported.

Either of the PUBLIC and PRIVATE keywords may be specified with or without the EXPORT keyword--the PROTECT keyword may not be specifeid with EXPORT. Here a examples of these options (just the Proc line itself):

   DCL cvtChar1 Proc PROTECT; // Subproc is Protected -- it is not exported from this module.
   DCL cvtChar2 Proc;         // Subproc is Protected -- same as using the PROTECT keyword.
   DCL cvtChar3 Proc PUBLIC;  // Subprocedure is PUBLIC and is exported as 'CVTCHAR3'
   DCL cvtChar4 Proc EXPORT;  // Subprocedure is PUBLIC -- same results as specifying only PUBLIC.
   DCL cvtChar5 Proc EXPORT('RPGOPEN_convertCharacter'); // Subproc is Public, exported as 'RPGOPEN_convertCharacter'
   DCL cvtChar6 Proc EXPORT PRIVATE; // Exported from this module, callable from within its parent *PGM or *SRVPGM. 
                                     //  It is not callable from outside of the *PGM or *SRVPGM object.

Prototyping is still necessary for subprocedures with case-sensitive name but I suppose a streamlined prototyping scheme could also be implemented. For example, to allow RPG V to call the C runtime function named 'atoll' (which has an all lowercase name) you wouldn't need to code the parameters, only the "header" for the prototype, as follows:

     DCL charToNum prototype('atoll') extern;

This prototype is named CHARTONUM and when used, calls the C runtime function named 'atoll'. The name is identified on the PROTOTYPE keyword. The EXTERN keyword says that this is an external subprocedure, go look for it in the Binding Directory and don't make me code the parameters.

An alternative syntax I thought of that might work is to have an external identifier keyword instead of a prototype reference.

  DCL charToNum extern('atoll') proc; 

There's probably a better syntax but I'm hopeful that the folks at IBM Canada will come up with something as simple as these choices.

5. New Class of Built-in Functions

There are three areas of built-in functions that need to be added to RPG V over RPG IV:

  1. Character Conversion
    1. TOASCII
    2. TOEBCDIC
    3. CVTCCSID
    4. TOLOWER
    5. TOUPPER
  2. System Utilities
    1. JOBLOG
    2. SndMsg
    3. RunCmd
  3. Enhanced Scanning
    1. REGEX
    2. SCANi

Character Conversion Built-in Functions

The ability to convert from ASCII to EBCDIC, from upper to lowercase and between CCSIDs should be built into the language. I recently wrote an article describing how to do CCSID conversion in RPGIV and incorporated it into the RPG Open runtime *SRVPGM. Read that article for details and how to download the code.

So many bad routines have been written to do these now fundamental tasks that its IBM's turn to implement them. In an RPG V, they should certainly be core functions.

System Utility Built-in Functions

I don't know how many thousands of shops have used my JOBLOG subprocedure, but it has become so common-place that there are actually shops using it who think it is part of RPG IV. It is note, I wrote it. It is a wrapper for the Qp0lzprintf API. I first introduced it in RPG xTools about 8 years ago, and then ported it to RPG Open and also published articles about it that contain the code.  RPG xTools also include tools to send program messages and run CL commands. These types of things should be built-in functions in RPG and I'd love to see them in RPG V.

Enhanced Scanning Built-in Functions

Last time I checked, it 2011, not 1977 as the %SCAN function seems to think. That was the era when UPPERCASE ONLY DATA ENTRY AND DISPLAY FILES WAS THE NORMAL THING TO SEE. Today people write in mixed case and sometimes all lowercase. RPG V needs to support case-insensitive scanning (heck, RPGIII should support that!) and while we're at it, why not add regular express support (also built into RPG xTOols) to the language.

Conclusions on RPG V

There are many new features that could be added to an "RPG V" language. I'm sure you have other priorities, but I tend to stay focused on those core features that are useful to the widest possible install base.

Call Me

Bob Cozzi is available to train your staff, on-site in RPG IV or SQL as well as perform consulting or contract programming. Currently shops are asking Cozzi to join them for 1 to 3 days of Q&A and consulting with their RPG staff. The Staff gets to ask real-world questions that apply to their unique situations. Rates and information are available here and you can contact Cozzi by sending him an email at: bob at rpgworld.com

Bob also accepts your questions for use in future RPG Report articles or content for midrangeNews.com. Topics of interest include: RPG IV, Web development with RPG IV, APIs, C/C++ or anything else IBM i development related (except subfiles, data areas and RPGII/III because Bob doesn't care about that stuff) write your questions using the Feedback link on the midrangeNews.com website. 

You can subscribe to RPG Report (we call it "follow") by visiting the RPG Report page on midrangeNews.com and then click the FOLLOW link in the table of contents for that page. To unsubscribe, simply click that same link. You must be signed up and signed in to midrangeNews.com to start following RPG Report.

Follow Bob Cozzi on Twitter

Return to midrangenews.com home page.
Sort Ascend | Descend

COMMENTS

(Sign in to Post a Comment)
Posted by: BrianR
Premium member *
Green Bay, WI
Comment on: Top New Features Needed in RPG V
Posted: 12 years 7 months 11 days 15 hours 21 minutes ago
Edited: Thu, 18 Aug, 2011 at 13:22:00 (4607 days ago)

 

Another idea is to use the same naming convention as subroutines (BEGSR/ENDSR) for data structures, prototypes, and procedures. Also optionally omit the DCL for subfields within a data structure or prototype. For example:

 

BEGDS lineItem Inz;

   item Char(5);

  desc varChar(50);

  price pkd(7,2);

  lineNo int;

ENDDS;

 

BEGPROT iconv EXTNAME('iconv'); // Prototype for iconv API.

    rtnCode Int return value;

    hConv Like(iconv_T) parm by Value;

    input Ptr parm by ref;

    inLen UINT parm by ref;

    output Ptr parm by ref;

    outLen UINT parm by ref;

ENDPROT;

 

BEGPROC cvtChar EXPORT('RPGOPEN_convertCharacter');

   BEGPI;

      rtnLength Int return value;

      inputValue Char(*) parm;

      inputLen Int parm const;

      outputValue Char(*) parm;

      outputLen Int parm const;

      rtnLength Int parm;

      fromCCSID Int Parm OPTIONAL;

      toCCSID Int Parm OPTIONAL;

   ENDPI;

// body of subprocedure goes here.

ENDPROC;

 

Posted by: bobcozzi
Site Admin ****
Chicagoland
Comment on: Top New Features Needed in RPG V
Posted: 12 years 7 months 6 days 13 hours 2 minutes ago

Brian, what if we used a PASCAL-esq BEGIN and END statement?

 

BEGIN  PROC(cvtChar) EXPORT('RPGOPEN_convertCharacter')

  ...

END PROC;

Posted by: BrianR
Premium member *
Green Bay, WI
Comment on: Top New Features Needed in RPG V
Posted: 12 years 7 months 5 days 14 hours 41 minutes ago

That also looks like a good idea.  The reason behind my suggestion is that I thought it was harder to distinguish between the different types of definitions when they all started with DCL and you had to search for the definition type after the name.  By putting the definition type first, it's easier to see what it is (at least to me), and it's easier to distinguish them from stand alone variables.

 

Does that reasoning hold water or am I being nitpicky?  Also, what do you think of the suggestion to allow ommission of "DCL" from variables inside of a more complex definition (data structure, prototype, etc.)?

Posted by: bobcozzi
Site Admin ****
Chicagoland
Comment on: Top New Features Needed in RPG V
Posted: 12 years 7 months 3 days 21 hours 47 minutes ago

Brian, if you implement the BEGIN/END thing, then I think it works without specifically coding the DCL for each subfield. But who knows in practice.  Basically my original proposal is based on the MI language and is somewhat how the "new" CL DCLxxx features were implemented (although they do use the reference to the host/parent variable)