Viewed from a top-down perspective, a REXX program consists of a series of statements. There are several different kinds of statements. The example we just examined consisted predominantly of instruction statements beginning with REXX keywords like DO, SAY, PULL, and IF. The only other statement type present was one assignment, distinguished by the presence of the = sign as the second token.

There are a couple of other program elements not illustrated yet. Labels, which consist of a symbol followed by a colon, may be mixed in among statements. Labels are used to define the start of an internal subroutine or function. Finally, there is a third type of statement, the command, which is unique to REXX among popular languages. This is literally the "every-thing else" category. REXX assumes that anything which is not an instruction, assignment, or label is a command. A command is not interpreted by REXX itself. Instead, it is passed to an external environment like an operating system or application program that is equipped to process commands. In a typical operating system like MVS, ALLOCATE, FREE, etc. are examples of commands.

REXX's ability to handle commands is very important, since it makes it possible to write batch procedures and application macros or scripts in REXX. Although other languages may-provide such capability through library functions, it is seldom an intrinsic part of the language as in REXX. Hence, other languages are unable to handle commands as naturally as REXX does. Since a command is just another type of statement, commands in effect provide a way to extend the language by introducing new directives. For instance, when REXX is used as a language for writing scripts for a communication program, the program itself may provide commands like SEND and WAIT. In use, these can be regarded almost as part of REXX itself. The benefit to an application program in employing REXX as a script or macro language is that the application needs to supply only its own specific commands, while all of the standard language facilities like variables, arithmetic, looping. and subroutines are provided by REXX.

Although REXX does not ultimately execute operating system or application commands, it can process command statements by substituting variable values and evaluating arithmetic or character string expressions. An example of this might be


This statement is actually a REXX expression that begins with a literal string and is followed by one symbol again followed by literal strings. The symbol is a variable name for which the current values are substituted. Finally, all strings are concatenated with single intervening blanks to produce a result. This statement is a command because it is not any of the other three types, so it is then passed to the default execution environment for execution. Since commands are application-specific, they play no further part in the structure of a REXX program as such. We will see the complete REXX procedure that accepts the values from the terminal user and allocated datasets—it is an extension of the COBOL debugging program that we wrote in the previous chapter:


The other three clause types (instructions, assignments, and labels) do, so we will concentrate our attention on them. There are currently about 25 (depending on how they are counted) different instruction types. Some of these instructions, specifically CALL, DO, EXIT, IF. ITERATE, LEAVE, RETURN, SELECT, and SIGNAL, define the flow of control within a REXX program. That is, they provide for testing, iteration, and subroutines. The remaining instruction types perform diverse functions like I/O (SAY, PULL), string parsing (PARSE), variable handling (DROP), and debugging (TRACE). Assignment statements can conveniently be regarded as another type of variable handling instruction, even though an assignment does not begin with a REXX keyword.

Testing is the simplest control flow construct. As illustrated in the earlier example, REXX uses an IF.. THEN . . . ELSE . . . format for this fundamental operation. The ELSE part of this construct is optional. There is no specific keyword (such as ENDIF in other languages) required to end an IF statement. Context and REXX syntax rules are sufficient to handle I Fs unambiguously. A complete IF statement might be:

complete IF statement

Here, RETURN (to return from a subroutine or main program) will always be executed, because only one statement can ordinarily follow THEN or ELSE. The exception to this, if multiple statements are required to follow THEN or ELSE, is a series of statements bracketed by DO... END. For instance:

complete IF statement

As always, the indentation is used only for clarity. As far as REXX is concerned, all statements could begin at the left margin. It is however, necessary to place the ELSE on a new line unless the clause following TH EN is terminated with a semicolon. IF statements can also be nested, as is commonly done when several alternatives must be handled:

complete IF statement

Here, the second IF statement occurs following the first ELSE. DO . , .END pairs can also be nested within each other and within IFs as appropriate. When a program must provide for testing many alternatives, a better way to do it than with nested I F statements is with the SELECT instruction. As with IF, the SELECT instruction really begins a compound statement, which might be something like:

complete IF statement

Here, after SELECT there is a series of WHEN . . . THEN . . . pairs, concluding with an OTHERWISE and finally an END. As with IF, some true/false condition follows each WHEN, and a statement to be executed when the corresponding condition is true follows THEN. Only the statement following the first true condition is executed. If none of the WHEN conditions is true, the statement following OTHERWISE is executed. If multiple statements need to be executed for each condition, they are grouped within DO . . . END pairs after THEN. SELECT statements can be nested within themselves and I Fs in any combination up to some implementation-defined level of complexity.

The next major type of control flow is iteration, i.e., looping. All REXX loops begin with a DO instruction. By itself, DO is used together with END to group statements. In that case, the enclosed statements are executed only once. However, there are more complex forms of DO that provide for a wide range of loop control. Loops may repeat the statements within their range a specific number of times (or forever). Loops may have a control variable that is incremented each time through. Loops may, finally, have logical conditions that cause them to terminate.

For instance, to sum N terms of a geometric series of powers of a variable X:

N terms of a geometric series of powers of a variable X

(X** 1 is the expression for exponentiation.) Here 1 is the control variable. It is initialized explicitly to 1 and incremented (implicitly) by 1 each time through the loop, terminating at the point where it would exceed the value of the variable N. Expressions could be used instead of constants or variables for the initial and limit values. Increments other than 1 can be specified explicitly.

If initial values, increments, and limits for a control variable are specified by a variable or expression instead of a constant, the quantities are valuated only once, before the loop is first executed. Sometimes it is desirable to decide whether to continue a loop based on quantities that are evaluated each time through. In that case, a sub-clause consisting of either of the keywords WHILE or UNTIL (or both) followed by an expression may be used. Thus, if we wanted to sum a geometric series up to the point where each summand is less than some limit, we might revise the preceding example to read:

N terms of a geometric series of powers of a variable X

(X must have a value less than 1 for this to work. And, of course, this example is needlessly inefficient because it evaluates X**l twice.) Conceptually, the difference between WHILE and UNTIL is that the former is tested at the top of the loop and the latter at the bottom. Note that no limit value for the control variable was supplied with a TO expression, but an increment was explicitly specified with a BY expression.

A DO loop does not need to have a control variable. Instead, it may simply specify how many times the loop is to be executed, using a constant, variable, or full expression. As we saw in the temperature conversion example above, this repetition count can even be forever. In this case, some means is still required for getting out of the loop. Any DO loop will be terminated by a RETURN instruction (which returns from a subroutine) or an EXIT instruction (which ends the REXX program). Another, less drastic way to get out of a DO loop is provided by the LEAVE instruction. It is particularly useful in a loop that is processing user input, for instance:

Do forever

Here, if the user types quit, the response is converted to uppercase by PULL, the LEAVE instruction is executed, and control passes to the next statement after the END instruction.

A similar requirement in a loop is to be able to go back to the start of the loop from somewhere in the middle, i.e., before the END instruction. This need is also frequently encountered when processing user input:

Do forever

Do forever

In this example, "|" is the symbol for logical or the compound condition in the IF instruction is true if either NUMBER < 1 or NUMBER > 10 is true. If the condition is true, the two instructions bracketed by a DO . . . END pair are executed. The second of these is ITERATE, which goes back to the start of the repetitive DO instruction. Note that the DO instruction on the fourth line of the example has no control variable, repetition count, or UNTIL/WHILE condition. So it is not a repetitive DO; it does not introduce a loop; and it is ignored by LEAVE and ITERATE instructions. These instructions always refer to the innermost repetitive DO loop in which they occur.

The third and last type of control structure in REXX is the procedure. A distinction is sometimes made between two types of procedures—subroutines and functions. A function must return a value, while a subroutine usually does not. However, in REXX the same procedure may sometimes return a value and sometimes not, so this distinction is not always relevant.

In most languages, procedures are identified unambiguously by the language syntax, by using special keywords or symbols. This is not the case in REXX. A procedure must begin with a label (i.e., a symbol followed by a colon), but not all labels introduce procedures. Whether or not a given label actually introduces a procedure depends on how it is used.

There are two ways a label can be used so as to be the beginning of a procedure, corresponding to the distinction between subroutines and functions. A subroutine procedure is invoked with the CALL instruction:

CALL instruction

The name of the procedure here is get-user-input, which is the label before the first instruction of the procedure. A function procedure is invoked by using the name of the procedure in a function reference within a REXX expression. A function reference consists of a symbol followed immediately (with no blank spaces) by a left parenthesis. The preceding example could be modified slightly to use a function reference instead of a subroutine call:

CALL instruction

There are several things to notice about this example. Although the procedure has no parameters, it was necessary to use parentheses around an empty argument list in order to identify it as a function reference. Also, the RETURN instruction that ends the procedure contains an expression (here just a variable name) that is returned as the value of the function. In the example, the returned value is then assigned to another variable.

There is one subtle point to note about REXX procedures. Unlike the END of a DO...END pair, the RETURN instruction in a procedure does not have a syntactic function. That is, the RETURN does not necessarily constitute the last line of the procedure. This is particularly true if the RETURN occurs inside some conditional (IF or SELECT) construct. In fact, in REXX it's not really possible to identify syntactically where the end of a procedure is to be found. The end of a procedure, as well as the beginning, is defined purely by the flow of control during execution rather than by syntax. This circumstance can be a source of confusion and errors in REXX programs, so programmers need to take extra care, using comments and blank lines, to make it very clear to a reader where a procedure begins and ends.

It is not necessary that all procedures used in a REXX program be defined (by a label) within the program. In the first place, REXX comes with a large number of predefined built-in functions. These functions deal mostly with input/output and with character string manipulation. The character string functions are noteworthy because they augment the already strong support REXX provides for working with character strings. Many character string operations, like substring, character replacement, and blank-delimited token parsing, are implemented as built-in functions. Many less common operations are, also. This support adds up to a great deal of power and flexibility for handling character strings with REXX.

In addition to built-in functions, REXX also allows for external procedures, i.e., procedures defined in other files. This gets into an area of the language that is implementation dependent. Most implementations of REXX allow invoking an external procedure as either a subroutine or function by using the file name in the CALL instruction or function reference. Procedures that are internal to another file, i.e., defined by a label within the file, usually cannot be invoked in this way. However, other implementation-specific mechanisms such as function packages are usually available for allowing REXX programs to access external procedures, which may be either system-wide or part of a specific application, and which may be written in other languages.

All rights reserved © 2018 Wisdom IT Services India Pvt. Ltd Protection Status

IBM Mainframe Topics