Implementing Stack in RPG

Pascal Plus Data Structures is the textbook I got the most value from in school. A program's data structures are probably the biggest factor in the complexity of the program's code. In recent years, RPG's data structure syntax has been expanded to include reuse of definitions and nested data structures. Such features allow the code to be more concise while being more expressive.

Having recently coded some RPG-ILE subprocedures to implement a linked list (see previous post), I decided to implement a stack as well. The stack is a LIFO (last in, first out) data structure manipulated by Push and Pop functions.

The first field declared in the program code, stackable, is the definition of field that is stored in the stack. Changing this one field definition should be the only change necessary to change the element stored in the stack and referenced by the Push and Pop subprocedures.

The program code shows an example of the use of stacks. Five names are pushed on to stack1 then three names are popped from stack1 and saved on stack2. A new name is then pushed on to stack1 and the saved names restored from stack2 back to stack1. PrintStack() then is executed to perform a non-destructive print of the values in stack1.

h option(*srcstmt:*nodebugio)                                        
 
d stackable       s              6A                                  
  
d Print           pr                  extproc('Stack.Print') 
d  stack                              likeds(stack1) 
 
d Pop             pr                  extproc('Stack.Pop') like(stackable) 
d  field                              likeds(stack1) 
 
d Push            pr                  extproc('Stack.Push') 
d  stack                              likeds(stack1)
d  field                              like(stackable) CONST          
                                                                     
d Empty           pr              N   extproc('Stack.Empty')               
d  stack                              likeds(stack1) 
                                                                     
d stack1          ds                  qualified INZ 
d  nodes                              like(stackable) dim(20)        
d  cursor                        3P 0                                
  
d stack2          ds                  qualified INZ                  
d  nodes                              like(stackable) dim(20)       
d  cursor                        3P 0                              
  
d amy             s                   like(stackable) inz('Amy   ')
d betty           s                   like(stackable) inz('Betty ')
d carrie          s                   like(stackable) inz('Carrie')
d denise          s                   like(stackable) inz('Denise')
d ertha           s                   like(stackable) inz('Ertha ')
d fannie          s                   like(stackable) inz('Fannie')
d x               s              3P 0                              
    
 /free                                                             
           Stack.Push(stack1 : amy);                                     
           Stack.Push(stack1 : betty);                                   
           Stack.Push(stack1 : carrie);                                  
           Stack.Push(stack1 : denise);                                  
           Stack.Push(stack1 : ertha);                                   
   
           for x = 1 to 3;                                         
              Stack.Push(stack2 : Stack.Pop(stack1));                          
           endFor;                                                 
 
           Stack.Push(stack1 : fannie);                                  
 
           dow not Stack.Empty(stack2);                                 
              Stack.Push(stack1 : Stack.Pop(stack2));                    
           endDo;                                            
 
           Stack.Print(stack1);
 
           *inlr = *on;             
 /end-free  
 *********************************************************** 
 *                                                           
 *********************************************************** 
p Push            b                   EXPORT                 
d                 pi                                         
d stack                               likeds(stack1)         
d item                                like(stackable) CONST  
 /free        
            stack.cursor += 1;                               
            stack.nodes( stack.cursor ) = item;              
 /end-free        
p                 e                                          
 *********************************************************** 
 *      
 *********************************************************** 
p Pop             b                                          
d                 pi                  like(stackable)       
d stack                               likeds(stack1)         
 /free 
            stack.cursor -= 1;                               
            return stack.nodes(stack.cursor + 1);            
 /end-free 
p                 e   
 *********************************************************** 
 * 
 *********************************************************** 
p Print           b                   EXPORT                 
d                 pi 
d stack                               likeds(stack1)         
d name            s                   like(stackable)        
  
d stack3          ds                  qualified INZ          
d  nodes                              like(stackable) dim(20)
d  cursor                        3P 0 
 
 /free 
        dow (not Stack.Empty(stack));                              
           name = Stack.Pop(stack);                                
           Stack.Push(stack3:name);                                
           dsply name;                                       
        endDo;                                              
 
        dow (not Stack.Empty(stack3));                            
           Stack.Push(stack : Stack.Pop(stack3));                         
        endDo;                                              
 /end-free                                                  
p                 e                                         
 ***********************************************************
 *  
 ***********************************************************
p Empty           b                   EXPORT                
d                 pi              N                         
d stack                               likeds(stack1)        
 
 /free 
            return stack.cursor = 0;                        
 /end-free            
p                 e 

Most RPG programmers have adopted RPG-IV, ILE subprocedures and free form coding so many may find nothing new here. But, for those not completely familiar with these new facets of RPG, let's review the densest line of code in the program and work out from there. I consider the two calls in the one line of code to be a busy line of code. This line

Stack.Push(stack2 : Stack.Pop(stack1));

calls the Pop subprocedure which returns a value that is passed to the Push subprocedure. In other words, the value popped from stack1 is pushed onto stack2.

The fact that a subprocedure (Pop) is called from the argument list of another subprocedure (Push) did impact the coding of Pop. The second parameter of Push had to be declared as CONST to eliminate the compiler error. This is for similar reasons that the parameter has to be declared CONST when it accepts a literal argument. In both cases it is because there is no variable to accept any changes to the argument made within the subprocedure.

p Push            b                   EXPORT                 
d                 pi                                         
d stack                               likeds(stack1)         
d item                                like(stackable) CONST

There is a small but very significant difference between the coding of the parameters (and return value) of subprocedures Push and Pop. The style used to declare parameters and return values makes it easy to overlook the differences between Push and Pop. Pop returns a value and this is coded on the D spec 'pi' (subprocedure interface) line shown below. Push's 'pi' line (above) does not have a type defined on it so it has no return value. Both subprocedures name have a parameter named stack. Push has a second parameter named item. Pop has only the one parameter.
p Pop             b                                          
d                 pi                  like(stackable)       
d stack                               likeds(stack1) 

Now that we've seen the mechanics of the return value, let's put it to work. By having the Push() subprocedure return the stack passed to it as shown here:
 /free
            Push(Push(Push(Push(stack1:amy):betty):carrie):denise);
            *inlr = *on;
 /end-free
p Push            b                   EXPORT                 
d                 pi                  likeds(stack1)   
d stack                               likeds(stack1)         
d item                                like(stackable) CONST  
 /free        
            stack.cursor += 1;                               
            stack.nodes( stack.cursor ) = item;  
            return stack;            
 /end-free        
p                 e

But, the compiler complains that the stack passed to it must be declared CONST and, because doing so would contradict our desire to update the stack, a compromise is reached in which CONST is used but the parameter is now a pointer to a stack.

RPG Linked List

A developer told me about a problem he was having and I told him I thought a linked list would help in creating a good solution. Below is the example I created for him. There are many perfectly acceptable ways of creating a linked list but, I had a particular model in mind. My goal was to create a linked list that would both retain its original sequence and present the altered sequence. I also wanted the implementation to be clean and simple.
The purpose of the linked list is to be able to resequence data and that is exactly what this assignment required. I created the moveAbove() function that accepts two arguments, the node to be moved and the node to be superceded in the list.
The linked list data structure is a list of girls' names. The list was initialized with the girls' names in alphabetical order - Amy, Betty, Carrie, Denise, Ertha and Fannie. Then moveAbove(5:2) is run. The results are shown in blue above the program code. The DSPLY statements show the name, the original seq# and the nextPointer (not really a pointer). Note the seq# is their original sequence and the data in the array is still in that order. All that moveAbove() does is reassign next values and the print function prints in the linked list sequence. I was taught that the program is more efficient since data is not moved. The function is extensible as the data element is referenced by a pointer.
DSPLY  Amy    001 005
DSPLY  Ertha  005 002
DSPLY  Betty  002 003
DSPLY  Carrie 003 004
DSPLY  Denise 004 006
DSPLY  Fannie 006 007
h option(*srcstmt:*nodebugio)                          
                                                       
d printList       pr                                   
                                                       
d moveAbove       pr                                   
d                                3P 0 VALUE            
d                                3P 0 VALUE            
                                                       
d topOfList       s              3P 0 inz(1)           
                                                       
d list            ds                  qualified dim(20)
d seq                            3P 0 inz              
d data                            *   inz(*NULL)       
d next                           3P 0 inz              
                                                       
d dsplyField      s             14A                    
d amy             s              6A   inz('Amy   ')    
d betty           s              6A   inz('Betty ')    
d carrie          s              6A   inz('Carrie')    
d denise          s              6A   inz('Denise')    
d ertha           s              6A   inz('Ertha ')    
d fannie          s              6A   inz('Fannie')    
                                                      
 /free                                              list(1).seq  = 1;             
           list(1).next = 2;             
           list(1).data = %addr(amy);    
           list(2).seq  = 2;             
           list(2).next = 3;             
           list(2).data = %addr(betty);  
           list(3).seq  = 3;             
           list(3).next = 4;             
           list(3).data = %addr(carrie); 
           list(4).seq  = 4;             
           list(4).next = 5;             
           list(4).data = %addr(denise); 
           list(5).seq  = 5;             
           list(5).next = 6;             
           list(5).data = %addr(ertha);  
           list(6).seq  = 6;             
           list(6).next = 7;             
           list(6).data = %addr(fannie); 
                                         
           moveAbove(5:2);               
           printList();                  
           *inlr = *on;                  
 /end-free                              
p printList       b                                  
d                 pi                                 
d x               s              3P 0                
d ptrName         s               *                  
d name            s              6A   based(ptrName) 
 /free                                               
        x = topOfList;                               
        dow list(x).data <> *NULL;                   
           ptrName = list(x).data;                   
           dsplyField = name                         
                      + %editw(list(x).seq:'0   ')   
                      + %editw(list(x).next:'0   ') ;
           dsply dsplyField;                         
           x = list(x).next;                         
        endDo;                                       
 /end-free                                           
p                 e                                  
 *                                                   
p moveAbove       b                                  
d                 pi                                 
d number1                        3P 0 VALUE          
d number2                        3P 0 VALUE          
d x               s              3P 0                
d ptrName         s               *                 
d name            s              8A   based(ptrName)                                     
 /free                                                                                   
                                                          // move 5 above 2              
        for x = 1 to %elem(list) ;                                                       
           if list(x).next <> 0;                                                         
                                                          // make 4 point to 6           
              if list(x).next = list(number1).seq;        // 4.next = 5                  
                 list(x).next = list(list(x).next).next;  // 4.next = 6.seq              
                 leave;                                                                  
              endIf;                                                                     
           endIf;                                                                        
        endFor;                                                                          
                                                                                         
                                                                                         
        for x = 1 to %elem(list) ;                                                       
                                                          // make 1 point to 5 and 5 to 2
           if list(x).next = list(number2).seq;           // 1.next = 2.seq              
              list(x).next = list(number1).seq;           // 1.next = 5.seq              
              list(number1).next = list(number2).seq;     // 5.next = 2.seq              
              leave;                                                                     
           endIf;                                                                        
        endFor;                                                                          
                                                                                         
 /end-free                                                                              
p                 e


Blog Archive