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.
2 Comments