Here's an introduction to one of the least known AS/400 HLLs--REXX.
Of all of the currently available high level languages (HLL) on the AS/400, REXX may be the least known. REXX is installed on your system as part of OS/400, but you may not have even known that it is there.
REXX stands for Restructured EXtended eXecutor language. It is an SAA language, which means that there is a version of REXX available for VM, TSO/E and the OS/2 environments. REXX was originally available in the mainframe environment, developed internally at IBM, apparently as an alternative to the command processing languages then available.
An Interpretive Language
All current implementations of REXX are interpretive languages. As you will see, REXX programs tend to look rather loose compared with the compiled language programs you are accustomed to on the AS/400. There is some work on developing a partial compiler for REXX; if you are interested in learning more about the problems of compiling REXX and some of the steps being taken to provide a compiler, refer to the article "Partial Compilation of REXX" in the IBM Systems Journal, Vol. 30, No. 3, 1991.
In form, REXX looks like PL/I. It includes some of the program control functions of PL/I (looping constructs, CASE constructs, etc.). In content, REXX includes many functions that are reminiscent of BASIC. These include many character string processing functions, mathematical processing and logic operations. REXX also has the feeling of BASIC, in that you can use REXX programs in simple prompt/get input programs. (In that respect, running a REXX program emulates prompting for parameters with OCL statements on the System/36!)
How the AS/400 Processes REXX
Because REXX programs are interpreted, the source must be available at run time. The convention is to create REXX source members in a source file named QREXSRC. This can be in any of your libraries or QGPL. It is suggested that the source member type be specified as "REXX." The reason for this is that OS/400 does some work at converting the source into its internal format as you exit from the source member. If you don't specify the source member type as REXX, then this conversion is not done when exiting the editor, but is done when the REXX program is called. This causes a slightly longer load time. On our B10, the difference was six seconds to load the internal format REXX program, eight seconds to load the REXX program that wasn't saved as a REXX member. And yes, you read that right, the load time was six seconds. The reason for this long load time is that OS/400 starts the REXX environment as you call a REXX program. It takes some time to establish the environment. We will discuss these performance implications later.
To start a REXX program by itself (that is, not called from another program), you use the OS/400 command, Start REXX Procedure (STRREXPRC). When you use STRREXPRC, you must specify the source member and the source file where the REXX program is stored. You can also specify parameters for the program, if any. Unlike compiled programs, such as CL or RPG, there is no checking to determine if you have passed required parameters. For example, if you compile a CL program that specifies a parameter on its PGM statement, and then try to call the CL program without a parameter, the system issues an error message. With REXX, the program simply starts.
You can also call REXX programs from other programs. The mechanism for calling REXX from other programs is the QREXX program. QREXX has two required parameters, similar to the STRREXPRC command: the source member name, which is 10 characters, and the source file name, which is 20 characters. The source file must be left justified in positions 1-10, the library must be left justified in positions 11-20. You can specify a value of *LIBL, *CURLIB or a library name.
Calling REXX programs from other programs can be more complicated than using STRREXPRC. If you want to pass parameters to the REXX program, the third parameter of QREXX is an argument string. The first two bytes of the string are a binary field that specifies the length of the following character field. The two bytes are immediately followed by the parameter string, which can be up to 32,767 bytes long. There are other parameters that you can use when calling QREXX. If you need to use this interface, refer to the Procedures Language (REXX) Reference.
Another interesting use of OS/400 REXX is as a command processing program (CPP). If you create your own commands, you can specify that a REXX program is to be used, rather than a CL program or another HLL program. If you prompt the CRTCMD command, and enter "*REXX" in the PGM parameter, you will see that the prompter then supplies parameters where you can specify the REXX member, source file and command environment to be used.
When to Use REXX
For most standard data processing problems, there may be little need to use REXX. Because we are usually working with database or display files, where input/output processing is done in terms of fixed length records, we have become accustomed to being able to easily get input. However, when there is a need to handle variable input, where the format may not be exactly specified, REXX may prove to be quite valuable. This is because REXX's string handling functions are much more powerful than the functions available in other HLLs.
As an example of how REXX provides superior string handling functions, refer to 1, which is reproduced from Appendix E of the Procedures Language (REXX) Programmer's Guide. In this example, the problem is to find the first sentence, delimited by a period, from a 50-character string named &INPUT. Any remaining text is to be placed into the variable &REMAINDER.
As an example of how REXX provides superior string handling functions, refer to Figure 1, which is reproduced from Appendix E of the Procedures Language (REXX) Programmer's Guide. In this example, the problem is to find the first sentence, delimited by a period, from a 50-character string named &INPUT. Any remaining text is to be placed into the variable &REMAINDER.
The CL programming to accomplish this is, to say the least, not obvious. You can determine that it does accomplish the job, but you will have to work through the fragment to discover that. By contrast, the REXX statement to accomplish the same job may not be obvious either, if you are unfamiliar with REXX. However, the REXX statement can be explained as follows:
"Parse Var" is a REXX language construct indicating that we want to parse a variable; the name of the variable is "input." We are going to parse up to the period embedded in "input"--this is shown by the period contained within single quote marks. The single period following "input" is used as a place holder: it indicates that we don't care about anything up to the first embedded period. The variable "remainder" follows the embedded period; that is, where the rest of the sentence will be placed.
It is true, for this example, that clever programmers may be able to use the QCLSCAN program or the SCAN operations available in various HLLs. However, any of those methods will certainly involve more coding than the one REXX line shown in 1 on page 19.
It is true, for this example, that clever programmers may be able to use the QCLSCAN program or the SCAN operations available in various HLLs. However, any of those methods will certainly involve more coding than the one REXX line shown in Figure 1 on page 19.
The capabilities of REXX for processing strings may be of special interest when you create commands that allow lists as parameters. Part of the problem of processing lists in CL programs is that you must interpret a binary value to determine the number of list elements. CL does not directly support binary values, so you need to write a function to convert the value. REXX, however, includes that function and many others that you can use to break out parts of character strings. By specifying a REXX program as the command processing program, you can perform all of the manipulations you require. When the REXX program is invoked as a CPP, all of the parameters from the command are passed to the REXX program, in keyword format. Your REXX program can analyze the parameters, then call other programs or commands to process your command, just as a CL CPP would do.
REXX Programming
The rules of REXX programming are quite simple, but I found it frustrating to use REXX when I first started. Where a normal programming language imposes strict rules, REXX is loose. For example, you do not declare or otherwise define variables. Variables are declared "as used." Strangely, a REXX variable can take on any attribute within the same program; your first assignment to the variable might be as a character string, followed several statements later using the same variable as the result of an arithmetic operation! Also, the REXX implementation of an array is unlike other HLL array definitions. In REXX, an array is defined and used as a compound symbol, which looks like a qualified name. At this point, I will quote from the reference, and leave you to draw your own conclusion:
"You can use compound symbols to set up arrays and lists of variables, in which the subscript is not necessarily numeric.... A useful application is to set up an array in which the subscripts are taken from the value of one or more variables, so effecting a form of associative memory (content addressable)." (!)
In terms of REXX instructions, there are only a handful, shown in 2 (page 20). However, these instructions are not quite as simple as they appear at first; several of the instructions include many options (such as the PARSE instruction). The control operations (DO, EXIT, IF, ITERATE, LEAVE, RETURN and SELECT) are used similarly to other HLLs. Other instructions are used for operations that are unique to REXX, such as PARSE, PULL, PUSH and QUEUE.
In terms of REXX instructions, there are only a handful, shown in Figure 2 (page 20). However, these instructions are not quite as simple as they appear at first; several of the instructions include many options (such as the PARSE instruction). The control operations (DO, EXIT, IF, ITERATE, LEAVE, RETURN and SELECT) are used similarly to other HLLs. Other instructions are used for operations that are unique to REXX, such as PARSE, PULL, PUSH and QUEUE.
Much of the power of REXX is in the functions. You will easily see that there is a preponderance of character string functions in 3 (page 20). You can also create your own functions within REXX programs.
Much of the power of REXX is in the functions. You will easily see that there is a preponderance of character string functions in Figure 3 (page 20). You can also create your own functions within REXX programs.
Trying REXX
The good news about REXX is that it is quite easy to try it. The program shown in 4 (TRYREXRX) is adapted from the Programmer's Guide, which lists the REXXTRY program in Appendix E. To use TRYREXRX, you should create a QREXSRC source file, then enter the statements shown in 4 in source member TRYREXRX. You should specify the source member type as "REXX," although you can change this and experiment on your system to determine the difference in load times as discussed.
The good news about REXX is that it is quite easy to try it. The program shown in Figure 4 (TRYREXRX) is adapted from the Programmer's Guide, which lists the REXXTRY program in Appendix E. To use TRYREXRX, you should create a QREXSRC source file, then enter the statements shown in Figure 4 in source member TRYREXRX. You should specify the source member type as "REXX," although you can change this and experiment on your system to determine the difference in load times as discussed.
When you enter a REXX program, you can use upper or lower case for any of the REXX keywords. Comments are delimited with the /* */ construct, as in CL. Blanks are not significant. If you want to, you can enter multiple REXX statements on the same line; you denote the end of a statement with a semicolon. However, you should let good sense prevail: multiple statements on the same line are quite difficult to read.
After you have entered the source, you can start TRYREXRX with the STRREXPRC command. Simply prompt for that command and fill in the SRCMBR and SRCFILE parameters, and the TRYREXRX program will begin. Rather than me explain how TRYREXRX works, you should simply use it and try some REXX statements. If you become interested in REXX, you will need to obtain the REXX manuals (which are included on the CD ROM) and study the material contained therein.
TRYREXRX will let you type in REXX statements and get an immediate response. This is probably the quickest way to learn about the language. Granted, it won't be of much use in trying out programming constructs such as DO or IF groups, but you can try statements such as PARSE and use the functions.
One of the simplest things to try is to type in a character string, then run a few operations against it. For example, you can type in a simple assignment statement, such as shown in 5 line A. Type in the assignment and press ENTER, and then...nothing happens! However, REXX now knows about your variable, so you can start experimenting. For example, you can follow up with a statement to count the number of words in your variable. This is shown in 5 line B. The REXX "SAY" statement means just that: say the value, which shows it to you.
One of the simplest things to try is to type in a character string, then run a few operations against it. For example, you can type in a simple assignment statement, such as shown in Figure 5 line A. Type in the assignment and press ENTER, and then...nothing happens! However, REXX now knows about your variable, so you can start experimenting. For example, you can follow up with a statement to count the number of words in your variable. This is shown in Figure 5 line B. The REXX "SAY" statement means just that: say the value, which shows it to you.
You can try a more complicated function, parsing the parts of the character string. Type in the example shown in 5 line A, then type in the statement shown in 5 line C. The PARSE statement tells REXX to parse the variable "address," and find four variables, "name," "street," "city" and "state." We indicate that these values are delimited by a comma. After entering this statement, you can use SAY to see the value of any of the variables. Try that in RPG! (Astute readers have noticed that we have used the variable "address," which is also the name of a REXX instruction and function. REXX knows by context what we mean. As I said, the rules are rather loose.)
You can try a more complicated function, parsing the parts of the character string. Type in the example shown in Figure 5 line A, then type in the statement shown in Figure 5 line C. The PARSE statement tells REXX to parse the variable "address," and find four variables, "name," "street," "city" and "state." We indicate that these values are delimited by a comma. After entering this statement, you can use SAY to see the value of any of the variables. Try that in RPG! (Astute readers have noticed that we have used the variable "address," which is also the name of a REXX instruction and function. REXX knows by context what we mean. As I said, the rules are rather loose.)
You can continue trying various REXX functions and operations, and when you want to leave the REXX environment, simply type "exit" and press ENTER.
Learning More About REXX
REXX, to me, is an oddity. It is like owning a drill press, but living in an apartment. It is a very fine tool for when I need it, but I don't have much opportunity to use it. This may change with increasing familiarity. Part of the problem of knowing about something new and useful is knowing when to use it. As the TRYREXRX program shows, you can use REXX to create a very simple interactive environment, which coincidentally is a requirement for a project I am working on. Whether or not REXX is the appropriate language, remains to be determined.
Apart from the aforementioned references on the CD ROM disk, you may want to consider getting the printed manuals. There are two manuals for the AS/400, the Programmer's Guide and the Reference. There are also several books available about REXX, most of which were written with the mainframe version in mind (although most of the text should be valid for learning about the AS/400 implementation of REXX). One of the books that I bought is Modern Programming Using REXX, which is a hodgepodge of REXX and "introduction to programming" material. The main problem I found with this work was the formatting of the program listings; they are quite difficult to read. Nevertheless, this book may be more approachable than the mainframe-oriented works.
It appears that REXX will be with us for quite some time, since it is an SAA language. We expect to have more to say about REXX in the future, and would welcome seeing any REXX programs that you use, or descriptions of how you use REXX on your system.
References Procedures Languages 400/REXX Programmer's Guide, SC24-5553 Procedures Languages 400/REXX Reference, SC24-5552 Modern Programming Using REXX (O'Hara and Gomberg), ISBN 0-13-597329-5 Also, the creator of REXX has a book: M.F. Cowlishaw, The REXX Language: A Practical Approach to Programming Prentice-Hall, 1985.
A Look at REXX
Figure 1 Comparison of CL string processing with REXX
Figure 1: Comparison of CL String Processing With REXX CL processing: DCL &INPUT *CHAR LEN(50) DCL &REMAINDER *CHAR LEN(50) DCL &X *DEC LEN(2 0) VALUE(1) DCL &L *DEC LEN(2 0) SCAN: IF ((%SST(&INPUT &X 1) *NE '.') *AND + (&X *LT 50)) THEN(DO) CHGVAR &X (&X + 1) GOTO SCAN ENDDO CHGVAR VAR(&L) VALUE(50-&X) CHGVAR VAR(&X) VALUE(&X+1) CHGVAR VAR(&REMAINDER) VALUE(%SST(&INPUT &X &L)) REXX processing: parse var input . '.' remainder
A Look at REXX
Figure 2 REXX instructions
Figure 2: REXX Instructions ADDRESS changes the destination of commands ARG retrieves argument strings provided to a program or routine CALL invokes a routine, controls trapping of conditions DO groups instructions together, optional iteration DROP "unassigns" a variable EXIT leaves program unconditionally IF conditional processing of instruction or group INTERPRET executes instruction ITERATE alters flow within DO loop LEAVE exits from DO loop NOP dummy instruction (target of THEN or ELSE) NUMERIC changes arithmetic processing OPTIONS passes special requests to language processor PARSE assigns data to variables PROCEDURE begins internal procedure PULL reads a string from the head of the external data queue PUSH stacks a string onto the external data queue QUEUE appends string to the end of the external data queue RETURN returns control from a REXX program or internal routine SAY writes to the default output stream SELECT conditionally executes one of several alternatives SIGNAL causes abnormal flow of control, or control condition trapping TRACE debugging aid
A Look at REXX
Figure 3 REXX functions
Figure 3: REXX Functions ABBREV checks for leading characters equal to test string ABS returns absolute value ADDRESS returns environment name of command processor ARG returns an argument string or information about arguments passed to program BITAND bitwise AND operation BITOR bitwise OR operation BITXOR bitwise EXCLUSIVE OR operation B2X converts binary string to hexadecimal CENTER/CENTRE centers a string COMPARE compares two strings CONDITION returns information about the current trapped condition COPIES copies a string C2D converts character to decimal C2X converts character to hexadecimal DATATYPE returns datatype of variable DATE returns current date DBCS double-byte character functions DELSTR deletes string within a string DELWORD deletes a word from string DIGITS returns setting of NUMERIC DIGITS D2C converts decimal to character D2X converts decimal to hexadecimal ERRORTEXT returns errortext associated with error number FORM returns current setting of NUMERIC FORM FORMAT formats a number FUZZ returns current setting of NUMERIC FUZZ INSERT inserts a string within another string LASTPOS returns position of last occurrence of one string in another LEFT returns a string of leftmost characters LENGTH returns length of string MAX returns largest of a list of numbers MIN returns smallest of a set of numbers OVERLAY overlays one string on another POS returns the position of one string in another QUEUED returns number of lines in external data queue RANDOM returns a random number REVERSE reverses a string RIGHT returns a string of rightmost characters SIGN returns a number indicating the sign of a number SOURCELINE returns number of final line in the source file SPACE returns a string with pad characters inserted STRIP returns string with leading and/or trailing characters removed SUBSTR returns substring of a string SUBWORD returns a substring of words starting at the specified word SYMBOL returns the state of a variable TIME returns the current time TRACE returns current trace options TRANSLATE returns a string with characters translated TRUNC truncates a number VALUE returns value of a symbol VERIFY determines if characters of a string are in another WORD returns a word WORDINDEX returns the position of the first character in a word WORDLENGTH returns the length of a word WORDPOS returns the word number of a word in a string WORDS returns the number of blank-delimited words in a string XRANGE returns a string of between "start" and "end" range X2B converts hexadecimal to binary X2C converts hexadecimal to character X2D converts hexadecimal to decimal SETMSGRC controls REXX processing *STATUS and *NOTIFY messages (AS/400 only)
A Look at REXX
Figure 4 The TRYREXRX program
/* */ parse source system howcalled pgm src parse arg arg say pgm 'allows you to interactively execute REXX instructions -' say 'each instruction string is executed when you press ENTER.' if arg ª='' then push arg else do say ' ' say 'Enter REXX instructions, or EXIT to end.' end trace = 'Off' border = copies('.',60) top: signal on syntax do forever parse pull line select when line = '' then say pgm': enter EXIT to end.' otherwise rc = 'X' call setline trace (trace) interpret line trace 'Off' if rc = 'X' then say border else say overlay)'RC = 'rc' ',border) end if arg ª= '' & queued() = 0 then exit end Setline: traceline = sigl return result Syntax: trace Off; say if sigl <= traceline then do say 'Invalid trace argument, set to "All".' trace - 'All' signal top end say 'Syntax error (RC =' rc':' errortext(rc)')' if arg ª= '' then exit rc else signal top
A Look at REXX
Figure 5 Things to try with TRYREXRX
Figure 5: Things to Try With REXXTRY A: address = 'Midrange Computing, 5650 El Camino Real, Carlsbad, CA 92008' B: say words(address) C: parse var address name ',' street ',' city ',' state say name say street say city say state
LATEST COMMENTS
MC Press Online