Use Rexx for scripting in 2023

Here are two simple programs to show how easy Rexx is to read, write, and maintain.
No readers like this yet.
women programming

WOCinTech Chat. Modified by Opensource.com. CC BY-SA 4.0

In a previous article, I showed how the Rexx scripting language is both powerful and easy to use. It uses specific techniques to reconcile these two goals that are often considered in conflict.

This article walks you through two example Rexx scripts so you can get a feel for the language. Rexx purports to be highly capable yet easy to work with.

An example of a Rexx script

The LISP programming language is famous for its overuse of parentheses. It can be a real challenge for programmers to ensure they're all matched up correctly.

This short script reads a line of LISP code from the user and determines whether the parentheses in the input are properly matched. If the parentheses aren't properly balanced, the program displays a syntax error.

Below are three sample interactions with the program. In the first, the LISP code I entered was correctly typed. But the next two contain mismatched parentheses that the Rexx script identifies:

Enter a line to analyze:
(SECOND (LAMBDA (LIS) (FIRST (CDR LIS)) ))
Parentheses are balanced

Enter a line to analyze:
((EQSTR (CAR LIS1) (CAR LIS2))
Syntax error:  too many left parens, not balanced

Enter a line to analyze:
(EQSTR (CAR LIS1) CAR LIS2))
Syntax error: right paren before or without left paren

Here's the Rexx program:

counter = 0                     /* counts parentheses        */

say 'Enter a line to analyze:'  /* prompts user for input    */
pull input_string               /* reads line of user input  */

length_of_string = length(input_string)

/* process each character of the input line, one at a time   */

do j = 1 to length_of_string while counter >= 0

  character = substr(input_string,j,1)
  if character = '(' then counter = counter + 1
  if character = ')' then counter = counter - 1    

end

/* display the appropriate message to the user               */

if counter = 0 then 
  say 'Parentheses are balanced'
else if counter < 0 then
  say 'Syntax error: right paren before or without left paren'
else 
  say 'Syntax error: too many left parens, not balanced'

First, the program prompts the user to enter a line of input with the say instruction. Then it reads it with a pull instruction.

The say and pull instructions are used for conversational input and output, or direct interaction with users. Rexx also supports character-oriented and line- or record-oriented I/O.

Next, the script uses the length function to place the length of the input line into the variable length_of_string.

The do loop processes each character from the input line, one at a time. It increments the counter each time it encounters a left parenthesis, and decrements it each time it recognizes a right parenthesis.

If the counter ends up as zero after processing the entire input line, the program knows that any parentheses in the input line match up correctly. If the counter is not 0 after processing, the input line has mismatched parentheses.

The final if statements display the proper message to the user. One could code these if statements in any number of styles, as per individual preference. (The main requirement is that whenever multiple statements are coded within a branch, they must be enclosed in a do...end group.)

This program shows that Rexx is free-form and case-insensitive. It does not rely on reserved words, so you're free to use common words like counter or character to represent variables.

The one key requirement Rexx does impose is that any function must immediately be followed by a left parenthesis. Examples in the program are the length and substr functions. Put a space between a function name and its following parenthesis, and Rexx won't recognize the function.

Outside of a few minimal requirements like these, Rexx requires very little from the programmer in terms of syntax, special characters, or restrictive coding rules.

Rexx programs look and read like pseudo-code. This makes them relatively easy to read and work with.

A real-world example of a Rexx script

Here's a program from the real world:

Les Koehler, a Rexx user, had a legacy accounting program that matched accounting records on hand against those that a vendor sent to him daily. The legacy program ran for several hours every day to process 25,000 records. It employed a sequential "walk the list" technique to match one set of records against the other.

Les replaced the legacy program with a Rexx script. The Rexx script performs matching by using associative arrays:

/* Create an associative array reflecting  */
/* the values in the first list of names   */
/* by Les Koehler                          */

flag. = 0               /* Create array, set all items to 0 */
do a = 1 to list_a.0    /* Process all existing records     */
  aa = strip(list_a.a)  /* Strip preceding/trailing blanks  */
  flag.aa = 1           /* Mark each record with a 1        */
end

/* Try to match names in the second list  */
/* against those in the associative array */ 

m = 0                  /* M counts number of missing names   */
do b = 1 to list_b.0   /* Look for matching name from LIST_B */
  bb = strip(list_b.b) /* Put LIST_B name into variable BB   */
  if \ flag.bb then do /* If name isn't in FLAG array       */
    m = m+1            /* add 1 to count of missing names    */
    missing.m = bb     /* add missing name to MISSING array  */
  end
end

missing.0 = m          /* Save the count of unmatched names */

Les was able to reduce processing time from several hours down to a matter of seconds.

The first line of code (flag. = 0) creates a new array called flag and initializes every element in that array to 0.

The array list_a contains all the existing accounting records. Its first element (list_a.0) by convention contains the number of elements in the array.

So the first do loop processes all elements in the array of existing records (list_a) and marks each of them as existing in the flag array. The statement flag.aa = 1 marks the content-addressable item in the flag array as present.

The second do loop peddles through each item in the set of new records, contained in the array called list_b.

The if statement checks whether an item from the second array of records is marked present in the flag array. If not, the program increments the number of items present in the new list of accounting records that do not exist in the old list of records. And it puts the missing item into the missing array: missing.m = bb.

The final statement (missing.0 = m) simply updates the number of items in the missing array, by convention stored in array position 0.

Rexx improvements

Why is this Rexx program so fast compared to the legacy code it replaces? First, the associative arrays allow direct lookup of a new record against the old records. Direct access is much faster than the sequential "walk-the-list" technique it replaced.

Secondly, all the array elements reside in memory. Once the files of the old and new accounting records have been initialized into the Rexx arrays, no further disk I/O is needed. Disk I/O is always orders of magnitude slower than memory access.

A Rexx array expands as much as memory allows. This script takes advantage of modern computers with seemingly endless amounts of RAM, and frees the programmer from managing memory.

Conclusion

I hope these two simple programs have shown how easy Rexx is to read, write, and maintain. Rexx is designed to put the burden of programming on the machine instead of the programmer. Yet the language still has plenty of power, due to the design techniques I've described in this series of articles.

For free Rexx downloads, tools, tutorials, and more, visit RexxInfo.org. You can join the Rexx Language Association for free.

This article is dedicated to the memory of Les Koehler, who was active with Rexx and the Rexx community since their very earliest days.

User profile image.
Howard Fosdick is an independent consultant who works hands-on as a DBA/SA. He's written several technical books, many articles, and is a popular conference speaker.

2 Comments

Another article on Rexx. So I have to repeat a comment from another Rexx article.

I used Rexx on an IBM mainframe a little over 30 years ago. One of the editors we used was Xedit. There was a PC variant of Xedit called Kedit. I still use that editor today. Its macro language was a subset of Rexx they called Kexx. An awesome scripting language.

Kedit was from Mansfield Software and they sold it up until last year (2021) I think.

I just wanted to say how much I appreciate your articles. Way back in 1989, when I was working at IBM as a consultant at the East Fishkill, NY mainframe manufacturing site, I started working with ISPF. Your book (Using IBM's ISPF Dialog Manager Under MVS, VM, and VSE) was an absolute lifesaver! Although now I am no longer working with mainframes, I still look to REXX / PowerShell for working with data. Thank you for all you do.

Creative Commons LicenseThis work is licensed under a Creative Commons Attribution-Share Alike 4.0 International License.