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.