Personal REXX User's Guide, Version 3.0 Chapter 5. Learning REXX August 1991 UIC ADN Computer Center Document #1615010 68 Pages REXX is a high-level programming language designed to support personal programming, operating system command files, macros and prototyping. REXX was originally provided by IBM as a component of the VM/CMS mainframe system, but is now available in many additional environments. At UIC, VM/CMS REXX is available on the UICVM CMS mainframe system, and Personal REXX is available at UIC on the Computer Center's public DOS personal computers and is also available for purchase by the UIC community under the UIC Computer Center's site license. (See the Computer Center document Personal Rexx Command Language for IBM Compatible DOS PC's for more information.) This document is Chapter 5, "Learning REXX" of the Personal Rexx Users Guide, provided by Quercus Systems with Personal REXX, Version 3.0, and also includes Chapter 4, "REXX Language Compatibility". The entire Personal Rexx Users Guide is available through the Computer Center's INFORM reference documentation facility (use INFORM with the search keywords pc rexx ). These two chapters are made available separately because they provide an excellent introduction to the REXX programming language and the portability of REXX programs to various platforms. Most of the information in this document applies to any implementation of the REXX programming language. Anyone who is interested in learning REXX will find it useful. For detailed information on the syntax and usage of REXX instructions, see the online HELP system on UICVM CMS. For information on the REXX instruction "inst", in UICVM CMS enter: HELP REXX inst Personal REXX User's Guide, Version 3.0 Using REXX at UIC page ii ======================================================================== Using REXX at UIC _________________ Running REXX Programs on UICVM CMS REXX programs to be run in VM/CMS systems like UICVM should be in CMS files with the filetype EXEC, and are executed by: in CMS: enter the filename of the CMS EXEC file as a command in CMS or from another EXEC: use EXEC filename where "filename" is the filename of the EXEC file, typed in all ____________ uppercase. __________ The first line of a REXX EXEC to be run on CMS must be a REXX comment. ______________________________________________________________________ REXX comments have the form: /* This is a REXX Program */ Only the /* and */ delimiters are required; you may replace the "This is a REXX Program" with anything you wish. These comments are not required in Personal REXX, but all the sample programs in Personal Rexx Users Guide do include them. The sample REXX programs discussed in this document are not available on UICVM CMS, but if you want to run them on CMS, you can either upload them to CMS from the PC (see Quick Facts for File Transfer Using FTP on the ADN Computer Center Public DOS Personal Computers for step by step instructions), or use XEDIT on CMS to enter them yourself. In either case, save the "xx'th" sample program in a CMS file with the filename LEARNxx and filetype EXEC, then run it in CMS as described above. Running Personal REXX and the LEARNxx Sample Programs on the Public DOS Personal Computers The sample REXX programs discussed in this document are available for your use on the Computer Center's public DOS personal computers: on the S: drive, in the \RX\LEARN directory. Thus, for example, the DOS pathname of the LEARN01.REX example program is: s:\rx\learn\learn01.rex To execute any of the LEARNxx programs referred to in this document on the Computer Center's DOS public personal computers: 1. If necessary, go to the DOS prompt. (On the public DOS personal computers with the IBM DOS Menu system, highlight "DOS Command Prompt" in the main menu and press Enter.) 2. To initialize Personal REXX, at the DOS prompt, enter: RXINTMGR 3. Enter: S: To move to the S: drive CD \RX To select the \RX subdirectory CD \LEARN To select the \LEARN subdirectory Personal REXX User's Guide, Version 3.0 Using REXX at UIC page iii ======================================================================== 4. Then to execute one of the LEARNxx programs, say LEARN01, enter: REXX LEARN01 You can use KEDIT on the public DOS PCs to look at or modify the LEARNxx.REX files. For example, to look at the LEARN01 program, while in the \RX\LEARN directory, enter: KEDIT learn01.rex or you can enter: KEDIT S:\RX\LEARN\learn01.rex from any DOS prompt. KEDIT is very similar to XEDIT; press F7 or F8 (or PgDn or PgUp) to go up or down in the file, respectively, and press F3 to leave KEDIT. If you change the file you are looking at, you will have to enter a command to leave KEDIT: Enter: To: QQUIT quit without saving your modifications. FILE C:filename.REX save the modified program with the filename "filename" on your C: drive; you can't write on the S: drive on the public DOS personal computers. For more information on using KEDIT, see Quick Facts for the DOS and OS/2 Text Editor KEDIT; use INFORM with the search keyword kedit. Differences Between Personal REXX and REXX on CMS Some of the more advanced information given below, particularly concerning the intereaction between REXX and the operating system in which it is running, is specific to Personal REXX and does not apply to REXX on CMS. These incompatibilites are pointed out when they arise. Also see Chapter 4, "REXX Language Compatibility", included below, for more information on the differences between Personal REXX, REXX on VM/CMS and other implementations of REXX. Personal REXX User's Guide, Version 3.0 Copyright Information page iv ======================================================================== Copyright Information _____________________ Copyright 1985-1991 Quercus Systems Quercus Systems P.O. Box 2157 Saratoga, CA 95070 (408) 867-REXX All Rights Reserved. Information in this document is subject to change without notice and does not represent a commitment on the part of Quercus Systems. The software described in this document is furnished under a license agreement. It may be used and copied only in accordance with the terms of the agreement. No part of this manual may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying and recording, for any purpose other than the purchaser's personal use without the written permission of Quercus Systems. Personal REXX and REXXTERM are trademarks of Quercus Systems. KEDIT is a trademark of Mansfield Software Group, Inc. PCED is a trademark of The Cove Software Group. LIMSIM is a trademark of Larson Computing. QEMM and DESQview are trademarks of Quarterdeck Office Systems. 386MAX is a trademark of Qualitas, Inc. MS-DOS and Windows are trademarks of Microsoft Corporation. OS/2 is a trademark of IBM. Sidekick is a trademark of Borland International, Inc. All other brand and product names are trademarks or registered trademarks of their respective companies. Personal REXX User's Guide, Version 3.0 Contents page v ======================================================================== Contents ________ Using REXX at UIC . . . . . . . . . . . . . . . . . . . . . . . . . . ii Running REXX Programs on UICVM CMS . . . . . . . . . . . . . . . . ii Running Personal REXX and the LEARNxx Sample Programs on the Public DOS Personal Computers . . . . . . . . . . . . . . . ii Differences Between Personal REXX and REXX on CMS . . . . . . . iii Copyright Information . . . . . . . . . . . . . . . . . . . . . . . . iv Chapter 1: Learning REXX . . . . . . . . . . . . . . . . . . . . . 1.1 Purpose . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1 About REXX . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1 Other References . . . . . . . . . . . . . . . . . . . . . . 1.1 Why Learn REXX? . . . . . . . . . . . . . . . . . . . . . . . 1.2 First Principles . . . . . . . . . . . . . . . . . . . . . . 1.2 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . 1.3 Running Personal REXX . . . . . . . . . . . . . . . . . . . . 1.3 A First Program . . . . . . . . . . . . . . . . . . . . . . . 1.3 REXX Structure and Syntax . . . . . . . . . . . . . . . . . . . 1.4 Clauses . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4 Tokens . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4 Simple Variables, SAY, and PULL . . . . . . . . . . . . . . . . 1.5 Simple Variables . . . . . . . . . . . . . . . . . . . . . . 1.5 SAY and PULL Instructions . . . . . . . . . . . . . . . . . . 1.6 Initial Variable Values . . . . . . . . . . . . . . . . . . . 1.7 Expressions, Operators, and Assignments . . . . . . . . . . . . 1.7 Concatenation Operators . . . . . . . . . . . . . . . . . . . 1.8 Assignment . . . . . . . . . . . . . . . . . . . . . . . . . 1.9 Arithmetic Operators . . . . . . . . . . . . . . . . . . . . 1.9 PARSE Instruction . . . . . . . . . . . . . . . . . . . . . . . 1.10 Compound Variables . . . . . . . . . . . . . . . . . . . . . . . 1.11 More Operators, Program Flow, Grouping Clauses . . . . . . . . . 1.13 Comparison Operators . . . . . . . . . . . . . . . . . . . . 1.13 Logical Operators . . . . . . . . . . . . . . . . . . . . . . 1.14 Control Structures and DO Groups . . . . . . . . . . . . . . 1.15 Conditional Instructions . . . . . . . . . . . . . . . . . . . . 1.15 IF Instruction . . . . . . . . . . . . . . . . . . . . . . . 1.16 IF/THEN/ELSE . . . . . . . . . . . . . . . . . . . . . . . . 1.16 SELECT Instruction . . . . . . . . . . . . . . . . . . . . . 1.17 DO Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.19 Simple Loops . . . . . . . . . . . . . . . . . . . . . . . . 1.19 Controlled Loops . . . . . . . . . . . . . . . . . . . . . . 1.20 Conditional Loops . . . . . . . . . . . . . . . . . . . . . . 1.21 Exiting Loops . . . . . . . . . . . . . . . . . . . . . . . . 1.22 More about PARSE . . . . . . . . . . . . . . . . . . . . . . . . 1.24 Parsing a Variable . . . . . . . . . . . . . . . . . . . . . 1.24 Parsing with Blank-delimited Words . . . . . . . . . . . . . 1.25 Personal REXX User's Guide, Version 3.0 Contents page vi ======================================================================== Parsing with Literal Patterns . . . . . . . . . . . . . . . . 1.26 Parsing with Column Numbers . . . . . . . . . . . . . . . . . 1.27 Parsing an Expression . . . . . . . . . . . . . . . . . . . . 1.27 Using Built-in Functions . . . . . . . . . . . . . . . . . . . . 1.28 Invoking Functions . . . . . . . . . . . . . . . . . . . . . 1.28 String Functions . . . . . . . . . . . . . . . . . . . . . . 1.29 Conversion Functions . . . . . . . . . . . . . . . . . . . . 1.31 Information Functions . . . . . . . . . . . . . . . . . . . . 1.32 User-defined Subroutines and Functions . . . . . . . . . . . . . 1.32 Invoking Subroutines and Functions . . . . . . . . . . . . . 1.33 Defining Subroutines and Functions . . . . . . . . . . . . . 1.34 Subroutines Versus Functions . . . . . . . . . . . . . . . . 1.34 EXIT Instruction . . . . . . . . . . . . . . . . . . . . . . 1.35 Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . 1.35 Variable Scope . . . . . . . . . . . . . . . . . . . . . . . 1.37 Commands to External Environments . . . . . . . . . . . . . . . 1.39 Advanced Features of REXX . . . . . . . . . . . . . . . . . . . 1.41 Numerics and Math . . . . . . . . . . . . . . . . . . . . . . 1.41 NUMERIC Instruction . . . . . . . . . . . . . . . . . . . . . 1.42 Debugging with the TRACE Instruction . . . . . . . . . . . . 1.42 INTERPRET Instruction . . . . . . . . . . . . . . . . . . . . 1.46 Personal REXX File Input and Output . . . . . . . . . . . . . . 1.47 Default I/O Streams . . . . . . . . . . . . . . . . . . . . . 1.47 I/O Routines . . . . . . . . . . . . . . . . . . . . . . . . 1.47 Personal REXX Utility Functions . . . . . . . . . . . . . . . . 1.52 Hardware Information Functions . . . . . . . . . . . . . . . 1.52 Hardware Access Functions . . . . . . . . . . . . . . . . . . 1.53 Miscellaneous Functions . . . . . . . . . . . . . . . . . . . 1.54 DOS Functions . . . . . . . . . . . . . . . . . . . . . . . . 1.55 What Next? . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.57 Chapter 2: REXX Language Compatibility . . . . . . . . . . . . . . 2.1 Implementation Limits . . . . . . . . . . . . . . . . . . . . . 2.2 Differences from IBM OS/2 REXX . . . . . . . . . . . . . . . . . 2.3 Differences from CMS REXX . . . . . . . . . . . . . . . . . . . 2.3 Differences from REXX 4.0 . . . . . . . . . . . . . . . . . . . 2.4 Differences in Source Program Handling . . . . . . . . . . . . . 2.4 Additional Language Features . . . . . . . . . . . . . . . . . . 2.5 Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.1 ======================================================================== Chapter 1 Learning REXX _____________ 1.1. Purpose This chapter is an introduction to REXX for programmers who are new to the language. It uses working programs to illustrate the essential elements of REXX. Our goal is for you to be able to write useful REXX programs as quickly as possible. Newcomers to REXX who take some time to browse through this introduction and try out the program examples will have built a firm foundation for mastering REXX. If you are already familiar with REXX, you can skip most of this chapter, but you should take a look at "Personal REXX Utility Functions" on page 1.52 We will concentrate on REXX fundamentals: variables, expressions, control flow, functions, and I/O. To avoid getting bogged down in details, we will omit the special cases and formal definitions that may be found in other references. At the end of this introduction we will cover the most important advanced REXX and Personal REXX features. You will find that REXX's syntax, control structures, and functions are very understandable in terms of many other modern high-level languages. While this chapter does not assume a knowledge of REXX or any other programming language in particular, it does assume that you have done some programming. If you aren't able to relate REXX to a language that you already know, then you may find it easier to begin learning REXX from a source such as O'Hara and Gomberg's Modern Programming Using REXX (Prentice-Hall, revised edition, 1988). 1.2. About REXX 1.2.1. Other References REXX was designed by M. F. Cowlishaw of IBM. His very readable The REXX Language, A Practical Approach to Programming, Second Edition (included with ) is the ultimate REXX language reference. If you want to learn more about REXX after this introduction, The REXX Language is the logical place to turn. If you want to learn more about using the special features of , you will want to continue reading in this User's Guide, particularly Chapter 8, "Personal REXX Utility Functions". Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.2 ======================================================================== 1.2.2. Why Learn REXX? REXX's standard features as defined in The REXX Language are very flexible and powerful, making REXX useful for a host of applications: personal programming, batch or shell programming, program macros, and prototype development. This introduction to REXX emphasizes the standard features of the language. Toward the end of this chapter we will introduce some of the implementation-specific aspects of . These added features are not necessary for writing many useful REXX programs, but they will allow you to get the most out of REXX in the DOS environment. When we discuss Personal REXX's features, we will make clear the distinction between them and standard REXX. 1.2.3. First Principles The primary design goal for REXX was ease of use by both professional programmers and every-day computer users. To help reach this goal, a set of principles were adopted. You may find it easier to learn REXX if you keep in mind the principles that shaped its design: * Readability: REXX supports modern structured programming concepts, and gives programmers great flexibility in the way they format programs. * Natural data typing: REXX assigns meaning to data according to its use, and does not require programmers to understand the underlying representation of data. * Symbolic manipulation: REXX's rich set of character manipulation operators and functions make it well suited to processing the textual information that people use. * System independence: All of the features of REXX behave the same, regardless of the hardware or software environment. But because REXX lets you issue environment-specific commands, REXX can act in very close concert with both the environment it is in and with other programs running alongside it. * Small size: Aside from its many built-in functions, REXX is a very compact language. This makes REXX easier to learn and master. For more about the design and history of REXX, see Part 1 "Background" in The REXX Language. Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.3 ======================================================================== 1.3. Getting Started 1.3.1. Running Personal REXX You will get the most out of this chapter by reading its sections in order, stopping to run those programs that interest you or that you don't understand completely from the text. All of the programs in this chapter are included on the Personal REXX distribution disk in the \LEARN subdirectory. The name for each program is given in a beginning comment, so you can easily find the corresponding file on disk. Instead of simply running the files in the \LEARN subdirectory, you might want to type in the example programs yourself and then run them. To experiment with the ideas discussed here, you might also want to modify the programs from this chapter or create your own programs. Before you can run the REXX programs given here, you will need to be sure that REXX is installed as described in Chapter 2, "Installing Personal REXX" of this User's Guide, and that you can run REXX and execute REXX source programs as described in Chapter 3, "Running Personal REXX". In particular, be sure that RXINTMGR has been loaded, that REXX.EXE is either in your path or in the current directory, and that the LEARNnn.REX programs are in your current directory. 1.3.2. A First Program The first example program, LEARN01.REX, simply displays on the screen the string Hello, world: /* LEARN01.REX -- write "hello" to screen */ SAY "Hello, world." /* END LEARN01.REX */ To make sure that you have properly installed and that you are able to run on your system, run LEARN01.REX. To do this, type rexx learn01 at the DOS prompt, and press the Enter key. If you have any problems with LEARN01.REX, you should review Chapter 2, "Installing Personal REXX" and Chapter 3, "Running Personal REXX" in this User's Guide. When you are confident that you can run REXX programs, you are ready to learn REXX. Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.4 ======================================================================== 1.4. REXX Structure and Syntax 1.4.1. Clauses A REXX program is made up of a series of clauses. Clauses are made up of a sequence of blanks and tokens. You can place multiple clauses on one line by separating them with semicolons (;). You can continue very long clauses on the next line by using a comma (). For example, REXX clauses to add two numbers and writes the result could be written in any of the following ways: /* LEARN02.REX -- show different clause formatting */ x = 4 + 5 /* (1) */ SAY "The answer is" x x = 4 + 5; SAY "The answer is" x /* (2) */ x = 4, /* (3) */ + 5; say, "The answer is" x /* END LEARN02.REX */ Usually you will find it easiest to place one clause on each line, as in the first sequence above. In this case, no special character is required to delimit the end of each clause. Clauses can begin with as many blanks as you want, allowing you to indent your program for readability. You can also use as many blanks as you want to separate the tokens in a clause. ________________________________________________________________________ Program Formatting: A clean, consistent style is a strong ally in writing readable, reliable programs. One of REXX's strong points is that it allows users great flexibility in formatting program text. The programs in this chapter demonstrate one possible style for formatting REXX programs. You may wish to modify this style to suit your own preferences. ________________________________________________________________________ 1.4.2. Tokens The tokens you will use most often for making up REXX clauses are: * Comments: REXX comments are delimited by /* and */ and can contain any sequence of characters on one or more lines. As the three comments in LEARN02.REX show, REXX comments can also share a line with executable clauses. You should begin all your REXX programs with a comment. Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.5 ======================================================================== This is good programming style, and may be necessary in order for other software to recognize your file as a REXX program. * Literal strings: REXX literal strings, delimited with the single-quote (') or double-quote ("), can include any characters. You can include single-quotes in strings delimited with double-quotes, and double-quotes in strings delimited with single-quotes. In LEARN01.REX, "Hello, world." is a literal string. Here are some others: "Enter client's name" '"No," he said.' "ABC" * Symbols: REXX symbols are identifiers made from alphanumeric characters and a small set of special characters. REXX does not distinguish characters of different case in symbols: lowercase alphabetic characters are converted to uppercase before REXX uses them. REXX symbols can stand for many things, depending on the context. Certain symbols such as IF and DO are keywords for special language instructions. Other symbols are the names of variables and functions that you or REXX define. * Numbers: REXX numbers are character strings of decimal digits. They may be preceded by a plus (+) or minus (-), and can include a period (.) to serve as decimal point. REXX numbers can also be expressed in exponential notation. Here are some valid REXX numbers: 123.654 -8.6 14 89e+64 * Operators: REXX has a set of special characters that serve as operators for creating expressions. There are the standard numeric operators such as plus (+), minus (-), multiply (*), and divide (/), as well as concatenation, comparison, and logical operators. 1.5. Simple Variables, SAY, and PULL 1.5.1. Simple Variables Let's look at a slightly more complicated REXX program that asks for the user's name and age, and then writes them out inside a message: /* LEARN03.REX -- get user's name and age into variables */ SAY "Please enter your name" PULL name SAY "Please enter your age" PULL age SAY name"," age "years is too long to be without REXX." /* END LEARN03.REX */ Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.6 ======================================================================== The symbols name and age are the names of simple REXX variables. Simple variable names are made up of one or more alphabetic, numeric, or special characters, and may not begin with a digit or period. REXX variable names, like all REXX symbols, are not case-sensitive. Some other simple REXX variable names could be: B INCREMENT1 top_of_file The values of all REXX variables, even integers and floating point numbers, are stored as character strings. This makes manipulating and formatting variables for display much easier, and it also means you don't have to worry about different variable "types". 1.5.2. SAY and PULL Instructions SAY and PULL are examples of REXX instructions -- keywords that are fixed elements of REXX. The syntax for SAY is: SAY [expression] REXX evaluates expression and writes the results to the screen, terminated by an end-of-line. If there is no expression then SAY just displays a blank line. SAY is the simplest REXX mechanism for placing information on the screen, and you will probably use it often. In LEARN03.REX the first two SAY instructions write literal strings. The last SAY evaluates a string expression consisting of the concatenation of several strings and displays the result. ________________________________________________________________________ Syntax Notation: Whenever you first see a new REXX instruction in this chapter, there will be an accompanying summary of its syntax. The syntax diagrams presented here try to distill the various forms of a REXX instruction into the one form that is practical for most situations. The syntax diagrams here are neither formal nor complete. For the full syntax of a REXX instruction you should refer to The REXX Language, Part 2, Section 7 "Keyword Instructions", and Appendix A, "REXX Syntax Diagrams". The full REXX syntax diagrams use several special symbols. We will make do with only one: square brackets ([,]). When part of an instruction appears in square brackets, that means it is normally optional. ________________________________________________________________________ PULL is REXX's simplest instruction for retrieving information from the user of a program via the keyboard. Its syntax is: PULL [template] Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.7 ======================================================================== PULL is actually an abbreviation for a variant of REXX's PARSE instruction. Later in this chapter we'll see how to work with PARSE, and how to use a template to easily process the user's input. For now, you can get a lot of work out of PULL just by knowing that PULL variable reads the next input line from the user, changes it to uppercase, and places the result in variable. 1.5.3. Initial Variable Values REXX variables always have an initial value. If you use a variable before assigning it a value, then the value of the variable is just its name in uppercase. This can be good and bad. It means that a REXX clause like SAY ENTER NAME will behave as expected, displaying ENTER NAME. This is because ENTER and NAME are variables that are initialized by REXX to their own names. However, suppose your program has already assigned the string value Smith to the variable NAME. In this case the clause above would display ENTER Smith -- probably not what you intended! Also, certain characters may have special meanings that cause problems when they are not part of a literal string. For example, SAY CONTINUE (Y/N) ? will not work as expected, because REXX interprets the Y/N as an attempt to divide the variable Y by the variable N. What you can do here is enter SAY 'CONTINUE (Y/N) ?' so that the text enclosed in quotes will not be evaluated. 1.6. Expressions, Operators, and Assignments In the last clause of LEARN03.REX we saw an example of concatenation, a REXX operation that merges two string values into one. REXX has many operators, which help you form expressions -- program statements that combine data in various ways to produce a result. You have probably seen numeric expressions such as 2 * (4 + 5) REXX has numeric expressions and many other types of expressions. Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.8 ======================================================================== REXX expressions consist of terms -- the data that is operated on -- and operators -- instructions that tell REXX what you want done. Terms can consist of literal strings, variable names, numeric constants, function calls, or other expressions bracketed by parentheses. REXX has four classes of operators: concatenation, arithmetic, comparison, and logical. 1.6.1. Concatenation Operators The REXX concatenation operators combine two strings to form one by appending the second string to the right-hand end of the first, with or without an intervening blank. The concatenation operators are notably easy to use. In REXX the blank (space) character can actually be interpreted as a concatenation operator. This makes clauses such as SAY name"," age "years is too long to be without REXX." extremely easy to write. The blank operator between the literal "," and the variable age combines these two strings into one, with an intervening blank. There are two ways available in REXX for concatenating values without any intervening blank. You can use the concatenation operator (||), or you can simply enter the two values right next to each other without any blanks between them ("abuttal"). In the statement above, name"," shows how you can concatenate two values by entering one immediately after the other, with no intervening spaces. What would you do if you wanted to concatenate two variables without an intervening blank? You couldn't put them one right after the other, because REXX would not be able to distinguish between the two separate variable names. That's where the concatenation operator comes into play. For example, /* LEARN04.REX -- concatenate two variables without space */ first_half = "Cow" last_half = "lishaw" SAY "REXX was designed by M. F." first_half || last_half"." /* END LEARN04.REX */ is able to put REXX's designer back together again, displaying: REXX was designed by M. F. Cowlishaw. Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.9 ======================================================================== 1.6.2. Assignment The first two lines of LEARN04.REX are examples of REXX assignments. A REXX assignment looks like this: variable = expression An assignment is the easiest way to change the value of a variable. REXX evaluates the expression and makes the result the new value of the variable to the left of the equal sign. You can assign the value of any expression -- string or numeric -- to any REXX variable. ________________________________________________________________________ Declaring Variables: Note that name and age in LEARN03.REX did not have to be declared. That is, we did not have to tell REXX anything about these variables before using them in the program. The variable declarations that are required by other languages can detract from the essential logic of smaller programs, and can make "throw-away" programs take longer to write. ________________________________________________________________________ 1.6.3. Arithmetic Operators REXX has the standard arithmetic operators -- add (+), subtract (-), multiply (*), and divide (/) -- as well as the integer divide (%), remainder (//), and exponentiate (**) operators. These operators can be applied to numeric constants, and to character strings, variables and expressions that evaluate to valid REXX numbers. /* LEARN05.REX -- calculate circle characteristics */ PI = 3.14159 SAY "Enter radius" PULL radius SAY "Area = " PI * (radius**2) SAY "Circumference = " 2 * PI * radius /* END LEARN05.REX */ Note that LEARN05.REX assigns a numeric constant to the variable PI for use in its calculations. Not only are the ensuing calculations clearer but, if the laws of the universe should suddenly change, the value of PI is easy to modify. The parentheses in the last clause make the order of expression evaluation obvious -- a good programming practice even if you have memorized the rules by which REXX evaluates numeric expressions. If you entered 5 for the radius, the program displays this: Area = 78.53975 Circumference = 31.41590 Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.10 ======================================================================== 1.7. PARSE Instruction The PARSE instruction for assigning data to variables is one of the most powerful and unique aspects of REXX. PARSE can make short work of tasks that would otherwise require complex loops and string operations. PARSE has many variations and applications, but we'll look at just one here: PARSE [UPPER] PULL template /* LEARN06.REX -- get user's name into two variables */ SAY "Please enter your first and last name" PARSE PULL firstname lastname SAY "The name entered was" lastname"," firstname"." /* END LEARN06.REX */ If you run this program and enter Ann Smith it displays The name entered was Smith, Ann. The PARSE PULL instruction waits for the user to enter a response, then assigns it to the variables firstname and lastname, preserving the case of the letters. In this case, PARSE PULL uses blanks to determine how to split the user's entry. The logic for splitting apart the user's entry into two separate words and then assigning them to variables -- which would require several steps in many other languages -- is handled in one step by REXX's PARSE. ________________________________________________________________________ PULL: The PULL instruction, which we saw in LEARN03.REX, is actually an abbreviation for PARSE UPPER PULL. PARSE UPPER PULL and PULL behave identically to PARSE PULL, except that they uppercase any alphabetic characters the user enters. ________________________________________________________________________ The real power of the PARSE instruction becomes obvious as the template becomes more complex. In the next example, PARSE uses a template that specifies the character string / rather than blanks to break up the input: /* LEARN07.REX -- split birthday into month, day, and year */ SAY "Please enter your birth date in the form: mm/dd/yy" PARSE PULL month '/' day '/' year SAY "The month entered was" month /* END LEARN07.REX */ Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.11 ======================================================================== PARSE PULL assigns the keyboard input to the variables month, day, and year using the template: month '/' day '/' year to break up the entered information at the slashes. If you entered 07/15/60, the program would write to the screen The month entered was 07 Later in this chapter, we'll look at another example of templates that include this kind of "literal pattern". We'll also examine other useful applications of the REXX PARSE instruction. 1.8. Compound Variables In the LEARN03.REX program we saw an example of REXX's simple variables. Simple variables behave much the same as variables in other programming languages. Most programming languages also give you data types for building more complex structures such as arrays, records, and lists. REXX has one easy-to-use data type for building these higher-level data structures: the compound variable. The rules for building a compound variable name in REXX are identical to those for building a simple variable, except that a compound variable has two parts: a stem and a tail. A stem is like any other variable name except that it ends in a period. A tail is a string of one or more symbols; when the tail is made up of more than one symbol, each symbol is separated from the next by a period. Here are some examples of compound variable names: color.4 john.age array.i.j When REXX retrieves the value of a compound variable it first generates a derived variable name. The name is derived by replacing any simple variables in the tail name by their values. The value of the compound variable is then the value associated with the derived name. An example will make this much clearer: /* LEARN08.REX -- use compound variables as arrays */ array.1.1 = 5 array.1.2 = 10 i = 1; j = 2 SAY "array.1.1 =" array.1.1 SAY array.1.2 "=" array.i.j "Right?" /* END LEARN08.REX */ Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.12 ======================================================================== When you run this program, it writes these results to the screen: array.1.1 = 5 10 = 10 Right? In the variable array.i.j in the last line, array. is the stem and i.j is the tail. To generate the derived name of the variable, REXX replaces the simple variables in the tail, i and j, by their values. So when REXX looks at the variable array.i.j, it sees array.1.2 because i has been set to 1 and j has been set to 2. Thus array.1.2 is the derived name for the compound variable array.i.j. Earlier we set the value of array.1.2 to 10, so that's what SAY displays. What would happen if we hadn't set i and j before telling REXX to SAY array.i.j? In that case i and j would evaluate to their own names in uppercase. This leaves REXX with array.I.J to evaluate. But we haven't set a variable called array.I.J anywhere in LEARN08.REX, so array.I.J evaluates to its name in uppercase -- or ARRAY.I.J. Thus, array.1.2 = 10 SAY array.1.2 "=" array.i.j "Right?" leaves us SAYing this: 10 = ARRAY.I.J Right? Wrong. ________________________________________________________________________ Stem Assignment: As mentioned earlier, the beginning of a compound variable name, up to and including the first period, is called the stem. You can set all possible compound variables derived from a given stem by assigning a value to the stem, for example array. = 0 After setting a stem variable this way, a reference to any compound variable with that stem to which you do not assign another value will return the value assigned via the stem. ________________________________________________________________________ REXX's compound variables are valuable when used as arrays, but are even more powerful than standard arrays because their subscripts do not have to be numeric. This allows you to create lists, trees, and tables that would require complex string manipulation and memory management in other languages. When used with nonnumeric subscripts, REXX's compound variables give you the ability to create data structures that are automatically indexed by their contents. For example, here is a program that uses two-letter state abbreviations to store and retrieve area codes: Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.13 ======================================================================== /* LEARN09.REX -- use compound variables to find area codes */ areacode. = "unknown" areacode.ME=207; areacode.NH=603; areacode.VT=802; areacode.RI=401 areacode.CT=203; areacode.MA="east 617 and 508, west 413" SAY "What state do you want the area code for?" SAY "(Enter a two-character abbreviation) " PULL state SAY "The area code for" state "is" areacode.state /* END LEARN09.REX */ The first line of LEARN09.REX is an assignment statement that sets all possible compound variables with the stem areacode. The six assignment statements that follow assign area codes for the New England states to the areacode variable, using character strings as subscripts. If you run LEARN09.REX and enter ct at the prompt, the program writes The area code for CT is 203 If you enter ny, the program displays The area code for NY is unknown 1.9. More Operators, Program Flow, Grouping Clauses Earlier we looked at several kinds of REXX operators, the special instructions that tell REXX how to manipulate data in expressions. The concatenation operators let you combine REXX string values, and the arithmetic operators let you perform complex calculations. Let's look at the two remaining kinds of REXX operators: the comparison and logical operators. 1.9.1. Comparison Operators The REXX comparison operators compare two values to each other and return a result of 1 if the comparison is true, or 0 if false. The LEARN10.REX program demonstrates the normal REXX comparison operators: Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.14 ======================================================================== /* LEARN10.REX -- demonstrate normal comparison operators */ SAY 2 = 2 /* equal */ SAY 2 = '02' /* equal */ SAY 2 = 2.0 /* equal */ SAY "TEST" <> "Test" /* not equal */ SAY 4 > 2 /* greater than */ SAY 1 < 2 /* less than */ SAY 4 >= 2 /* greater than or equal to */ SAY 1 <= 1 /* less than or equal to */ /* END LEARN10.REX */ All of the SAY instructions in LEARN10.REX display 1 on the screen, meaning that the expression is true. Note that character comparisons are case-sensitive: "TEST" and "Test" are not equal. ________________________________________________________________________ Strict Versus Normal Comparison: Because REXX handles all values whether numeric or not as character strings, it needs two sets of comparison operators: strict and normal. The strict operators, which have one of the operator characters doubled (like ==, >>, <<), test whether all characters in two string values are precisely equal (taking into account all characters in the strings, including any leading or trailing blanks). The normal operators, however, will perform a numeric comparison if both terms are numeric. Otherwise, the normal operators compare terms as character strings, ignoring leading and trailing blanks. ________________________________________________________________________ 1.9.2. Logical Operators The final set of REXX operators, the logical operators, take one or two true (1) or false (0) values and combine them in a logical operation to return another true or false value. Often it is the comparison operators that provide the true or false values that the logical operators need to operate on. The REXX logical operators are: & And Returns 1 if both terms are true; 0 otherwise. | Inclusive Or Returns 1 if either term is true; 0 if both are false. && Exclusive Or Returns 1 if only one term is true; 0 if both are false or both are true. \ Not Returns 1 if the term is false; 0 if true. Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.15 ======================================================================== You'll see the logical operators at work in the programs that follow. 1.9.3. Control Structures and DO Groups REXX has a complete set of control structures. These are built-in language instructions that let you control the order in which clauses are executed. Control structures are very important: without them you could write only programs that begin execution at the top, proceed downward to the bottom, then stop. You can use the REXX control structures to control flow of execution based on conditions you check, or alternatives you provide. You can also use them to loop repetitively through a set of clauses, until a certain condition or count is reached. Before we look at the first REXX control structure, let's consider the simplest form of a REXX "DO group": a set of clauses surrounded by the DO and END keywords. The DO group is also used to create loops in REXX, but in its simplest form it just groups multiple clauses together. A DO group can be used anywhere in a REXX program where normally only one clause would be allowed. Thus you could package three REXX clauses such as x = 1 y = x + 3 SAY y into a DO group like DO x = 1 y = x + 3 SAY y END and use the three clauses anywhere you could normally use just a single clause. We haven't yet seen any instances where we need a DO group, but in the next sections we will. 1.10. Conditional Instructions REXX gives you two fundamental methods for controlling program flow based on tested conditions: the IF and SELECT instructions. Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.16 ======================================================================== 1.10.1. IF Instruction The syntax for the REXX IF instruction is IF expression THEN instruction [ELSE instruction] The IF instruction tests an expression and executes an instruction (or group of instructions) based on the result. The expression tested must evaluate to either true (1) or false (0). If the expression is true then the instruction immediately following the THEN is executed. If the expression is false, and there is no ELSE portion, then the body of the IF is bypassed entirely. /* LEARN11.REX -- show simple IF construct */ SAY "Enter age: " PULL age IF age < 16 THEN SAY "Too young to drive." SAY "Done." /* END LEARN11.REX */ This program prompts the user for an age, and places it into the age variable. If the age is less then 16 the program displays Too young to drive. Done. If the age is greater than or equal to 16, the program just displays Done. The expression age < 16 is an example of the REXX "less than" comparison operator in action. 1.10.2. IF/THEN/ELSE The IF instruction can also contain an ELSE portion. If the tested expression is not true, then the clauses following the ELSE are executed. By nesting IF/THEN/ELSE instructions and grouping clauses with DO groups, you can create more-complex logical tests. Here's an example of IF/THEN/ELSE that also uses DO groups in the THEN and ELSE clauses: Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.17 ======================================================================== /* LEARN12.REX -- voter registration screening with nested IF's */ SAY "Enter age:" PULL age IF age >= 18 THEN DO status = " vote." msg = "Please proceed to the registration desk." END ELSE DO status = "not vote." msg = "See you when you're 18." END SAY "You can"status SAY msg /* END LEARN12.REX */ LEARN12.REX tests the user's age. If the age given is 18 or more, then the variable status is set to " vote" and the variable msg is set to Please proceed to the registration desk. Else, status is set to "not vote" and msg is set to See you when you're 18.. If you run LEARN12.REX and enter 20, the program displays You can vote. Please proceed to the registration desk. If you run LEARN12.REX and enter 16, the program displays You cannot vote. See you when you're 18. ________________________________________________________________________ THEN and ELSE: The format of IF/THEN/ELSE is flexible. In addition to the format shown in the syntax diagram on page 1.16, you can begin a new line before or after the keyword THEN, and you can also begin a new line after the keyword ELSE. (But you can't have both the THEN clause and the ELSE clause on the same line, unless a semicolon precedes the ELSE.) For a full specification of the syntax of IF/THEN/ELSE, see the entry for IF in Part 2, Section 7 of The REXX Language. ________________________________________________________________________ 1.10.3. SELECT Instruction You can create almost any logical test using the REXX IF instruction and the comparison and logical operators. However, there are certain situations -- such as testing menu choices or long lists of values -- where nested IF instructions are bulky and inefficient. Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.18 ======================================================================== For situations where you must test among many alternatives, REXX provides the SELECT instruction. The syntax for SELECT is: SELECT WHEN expression THEN instruction WHEN expression THEN instruction WHEN expression THEN instruction . . . [OTHERWISE instructionlist] END Each WHEN expression THEN instruction just like an IF/THEN construction. If the expression evaluates to true (1), then the instruction -- which may also be a set of instructions in a DO group -- is executed. Control then passes to the END, bypassing any other WHEN or OTHERWISE instructions. If the expression evaluates to false (0), then control passes to the next WHEN. If none of the WHENs are true, then there must be an OTHERWISE (or else REXX will report an error). In this case, the instructionlist associated with OTHERWISE is executed. Here's an example of the SELECT instruction in use: /* LEARN13.REX -- use SELECT to find area codes */ NULL = "" SAY "What New England state do you want the area code for?" PULL state SELECT WHEN state = "CT" THEN code = "203" WHEN state = "MA" THEN code = "617, 508 or 413" WHEN state = "ME" THEN code = "207" WHEN state = "NH" THEN code = "603" WHEN state = "VT" THEN code = "802" WHEN state = "RI" THEN code = "401" OTHERWISE code = NULL END IF code <> NULL THEN SAY "The area code for" state "is" code ELSE SAY "The New England states are ME, NH, VT, CT, RI, and MA" /* END LEARN13.REX */ In LEARN13.REX the SELECT instruction executes one of seven alternatives made up of six WHENs and one OTHERWISE. If the user enters a value for Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.19 ======================================================================== the variable state that is equal to one of the six New England states, then the variable code is set to the appropriate area code. If state doesn't equal one of the known six, then OTHERWISE sets code equal to a null string. At the end of the program, an IF instruction tests whether code is equal to NULL or not to see if the program recognized the state the user entered. If you run LEARN13.REX and reply with ct, the program writes to the screen The area code for CT is 203 1.11. DO Loops After learning how to use REXX for testing conditions and choosing among alternatives, you'll quickly find a need for looping: executing a group of clauses repeatedly until a certain condition or count is reached. The good news is that unlike many other languages, in REXX all loops are implemented with one instruction: DO. The bad news is that DO is the most complicated REXX instruction, and remembering all the variations can be hard at first. You've already seen one simple variant of DO for grouping clauses. We'll divide the remaining variants into three cases -- simple, controlled, and conditional loops -- and explore each separately. 1.11.1. Simple Loops One syntax for the simple DO loop is DO expression instructionlist END In this form of the DO loop, the value of expression is evaluated and the instruction-list is executed that many times. The expression must be a whole number greater than or equal to zero. The other possible syntax for the simple DO loop is DO FOREVER instructionlist END In this form, the instruction list is executed until one of the special REXX instructions -- LEAVE, EXIT, or RETURN -- is executed. Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.20 ======================================================================== /* LEARN14.REX -- simple DO loop */ DO 4 SAY "hello" END /* END LEARN14.REX */ When this program executes, the instruction within the DO/END is executed four times. The result is: hello hello hello hello 1.11.2. Controlled Loops In addition to simple DO loops, REXX provides controlled DO loops that take advantage of a control variable. The DO loop in LEARN15.REX uses the variable ctrl in order to display to the screen the first ten integers in ascending order: /* LEARN15.REX -- count to 10 with controlled loop */ DO ctrl = 1 TO 10 SAY ctrl END /* END LEARN15.REX */ The syntax for the controlled DO loop is: DO name = initial [TO terminal] [BY increment] instructionlist END In this form of the DO loop, a control variable name gets an initial value initial and is then stepped through a series of values by an amount increment until it reaches an ending value terminal. The step amount can be either positive or negative, so that the loop can step up or step down. The default step is +1. The expressions initial, terminal, and increment must all be numbers. Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.21 ======================================================================== /* LEARN16.REX -- use nested controlled loops to write */ /* a multiplication table to the screen */ limit = 3 DO x = 1 TO limit /* find multiples up to limit */ DO y = 1 TO limit SAY x "x" y "=" x * y END y /* control variable */ END x /* control variable */ /* END LEARN16.REX */ When run, this program displays 1 x 1 = 1 1 x 2 = 2 1 x 3 = 3 2 x 1 = 2 2 x 2 = 4 2 x 3 = 6 3 x 1 = 3 3 x 2 = 6 3 x 3 = 9 ________________________________________________________________________ Loop Style: REXX allows you to change the value of the control variable while within a controlled DO loop, possibly affecting when the loop will terminate. However, in general this is a bad programming practice, because it makes the behavior of the loop difficult to understand. REXX allows you to include a loop's control variable in the loop's END clause, thereby obtaining automatic checking of loop nesting. You can see this in the two nested loops of LEARN16.REX. If the variable you give doesn't match the one at the head of the corresponding DO loop, then REXX reports an error. ________________________________________________________________________ 1.11.3. Conditional Loops The syntax for the conditional DO loop is DO WHILE expression instructionlist END or DO UNTIL expression instructionlist END Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.22 ======================================================================== REXX evaluates the expression each time around the loop. With WHILE, the loop continues to execute the instruction list as long as the expression evaluates to true (1). With UNTIL, the loop continues to execute the instruction list until the expression evaluates to true (1). In a WHILE loop the expression is evaluated before the instruction list is executed, whereas in an UNTIL loop the expression is evaluated after. Thus if a WHILE loop expression is false when initially tested, the instruction list won't be executed at all. But an UNTIL loop instruction list will always be executed at least once. ________________________________________________________________________ Complex Loop Control: Technically, the WHILE and UNTIL conditional phrases can be added to the simple and controlled loop types that we've already seen. But in actual practice you'll rarely need to mix so many controlling conditions. ________________________________________________________________________ Here is an example of a DO WHILE loop: /* LEARN17.REX -- echo keyboard input to screen */ PARSE PULL text /* get first line of input */ DO WHILE text \== "" /* while input is not null */ SAY text /* echo input to screen */ PARSE PULL text /* get next line of input */ END /* END LEARN17.REX */ LEARN17.REX continues to read in lines of text from the keyboard and echo each line to the screen while the user enters non-null strings. (A null string is entered when the user hits Enter without entering any text on the line). Since the WHILE condition is tested before the instruction list is executed, this program could end immediately if the user's first keystroke is Enter. 1.11.4. Exiting Loops On occasion you need to change the flow of control of a program inside a DO loop. In some other languages this situation is handled with a GOTO instruction, but REXX gives you a more structured method: the ITERATE and LEAVE instructions. Even in situations where you don't technically need ITERATE or LEAVE to accomplish your objective, they may help you avoid a confusing nest of IF instructions. The syntax for ITERATE is: ITERATE [name] Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.23 ======================================================================== ITERATE causes REXX to stop executing the instruction list within a DO loop, then pass control back up to the DO exactly as if the END clause had been reached. The DO is executed normally, and if its conditions are met, the instruction list is executed again. The syntax for LEAVE is: LEAVE [name] LEAVE causes REXX to immediately exit a DO loop. Control resumes at the instruction immediately following the END corresponding to the exited loop. When giving the ITERATE or LEAVE instructions you can specify the name of the control variable for a currently active loop. If a name is given with ITERATE then REXX steps the identified loop, and any loops inside that one are terminated. If a name is given with LEAVE, then REXX terminates the identified loop (and any loops inside it), passing control to the clause following the END for the loop. If no name is given, then ITERATE and LEAVE apply to the innermost active loop. Let's look at a simple REXX program that illustrates ITERATE and LEAVE. LEARN18.REX uses LEAVE to enforce a maximum iteration count, regardless of what the user enters, and it uses ITERATE to skip over odd integers: /* LEARN18.REX -- write even integers up to maximum */ MAXIMUM=10 SAY "Enter count:" PULL count DO i=1 TO count IF i > MAXIMUM THEN LEAVE IF \DataType(i/2,"Whole") THEN /* test for odd integer */ ITERATE SAY i /* display if even */ END /* END LEARN18.REX */ If you run this program and enter 15 for the count, it displays 2 4 6 8 10 Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.24 ======================================================================== In the second IF test we see the NOT (\) logical operator applied to the result from the REXX built-in function DATATYPE(). We'll discuss the REXX built-in functions in detail in a later section. For now, it's enough to know that DATATYPE() evaluates i/2, telling us if it is a whole number. The program ITERATEs the DO loop if i/2 is not whole (which would mean that i is odd). 1.12. More about PARSE In an earlier section we glanced at REXX's powerful PARSE instruction for assigning data to variables. We saw the PARSE PULL variant for taking input from the keyboard, and we saw how to use simple templates to break up that input by blank-delimited words or literal strings. In this section we will see how to make PARSE take its input from sources other than the keyboard, and how to have it parse what it gets with more advanced templates. In general, when you use the PARSE instruction you must specify a source for the data to PARSE -- such as the keyboard, a variable, or an expression -- and a destination for the parsed data -- a template that controls exactly how data is split up among variables. A PARSE template is made up of alternating pattern specifications and a set of variable names. Blanks, literals, and column numbers can all be used to establish patterns. In this section we'll look at the different ways to establish patterns, and how to parse variables and the values of expressions. 1.12.1. Parsing a Variable PARSE can get its input data from a variable. Here is an example of using PARSE to split the contents of a variable into two other variables: /* LEARN19.REX -- break up a variable into words */ name = 'Ann Smith' PARSE VAR name firstname lastname SAY firstname lastname /* END LEARN19.REX */ When run, LEARN19.REX displays Ann Smith In LEARN19.REX, PARSE uses a template consisting of two variables, firstname and lastname, to break up another variable by blank-delimited words. The first word of name goes into firstname, and the remaining Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.25 ======================================================================== words (in this case only one), go into lastname. The program then SAYs the two variables to the screen so that their contents appear to be together again. The syntax for using PARSE on a variable is PARSE [UPPER] VAR name template 1.12.2. Parsing with Blank-delimited Words Let's take a closer look at how PARSE matches blank-delimited words. The program below demonstrates two useful PARSE abilities: the special treatment of the last variable in a template, and the use of the period (.) as a dummy placeholder. /* LEARN20.REX -- break up file statistics */ taxinfo="NY 000-11-2222 Smith" PARSE VAR taxinfo state id /* id gets '000-11-2222 Smith' */ SAY id PARSE VAR id ss_num . /* period holds place */ SAY ss_num /* END LEARN20.REX */ Running this program displays 000-11-2222 Smith 000-11-2222 The first PARSE instruction in LEARN20.REX demonstrates how PARSE handles the last variable in a template. PARSE assigns whatever remains of the input string (possibly several words) to the last variable. In this case, id gets both the social security number and the name from taxinfo. This is because taxinfo consists of three words but there are only two variables in the PARSE template. The second PARSE instruction shows the use of the period (.) in a template. A period can appear wherever a variable can in a template, but any value that would have been assigned to a variable in that position is ignored. In LEARN20.REX the period after ss num causes Smith, the remainder of the input string after the social security number, to be collected and discarded. The net result is that ss num gets assigned just the first word of the value of id. ________________________________________________________________________ Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.26 ======================================================================== Running Out of String Data: What happens if there are fewer words in the input data than variables in the template? Because REXX always assigns every variable in a template a new value, the unused variables will be set to a null string (""). ________________________________________________________________________ 1.12.3. Parsing with Literal Patterns Besides matching blank-delimited words, PARSE can also use templates that contain literal patterns, where variable names are separated by strings enclosed in quotes. Input is placed into a variable named in the template until the input matches the literal pattern. Any later input is read into the next variable, and so on. /* LEARN21.REX -- break up variable using literal patterns */ SAY "Enter your name and address in the form" SAY " Name, Address :Zip_code" SAY "" PARSE PULL name ',' address ':' zip SAY address /* END LEARN21.REX */ The variable name is assigned all the input up to (but not including) the first comma, address is assigned all input between the first comma and a following colon, and zip is assigned all input after that colon. For example, if you enter rexx learn21 and then type in Ann Smith, 123 Main St. Small Town, CT :06000 for PARSE PULL to use as its input data, LEARN21 will display 123 Main St. Small Town, CT ________________________________________________________________________ Running Out of Pattern Matches: What happens when a pattern you give in a template matches nothing in the input string? In that case REXX considers the pattern to match the end of the string. Thus the variable preceding the pattern is assigned the remainder of the string, and any variables following the pattern are set to null. For example, if you run LEARN21 and enter Ann Smith, 123 Main St. Small Town, CT 06000 Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.27 ======================================================================== with no colon preceding the zip code, the variable address is set to 123 Main St. Small Town, CT 06000 and zip is set to a null string. ________________________________________________________________________ 1.12.4. Parsing with Column Numbers In addition to breaking up an input string based on blank-delimited words or literal patterns, PARSE can break up a string based on numeric column positions, as in the following program: /* LEARN22.REX -- break up variable using column numbers */ info="Smith,A. 111-22-3333 203-555-1222" PARSE VAR info name 16 ssn 31 phone SAY phone /* END LEARN22.REX */ When run, LEARN22.REX displays 203-555-1222 The variable info is broken into the variables name, ssn, and phone as follows: name gets the characters up to position 16, ssn gets the characters from position 16 up to column 31, and phone gets the characters from position 31 on. Any time you use unsigned numbers in a template, REXX interprets them as absolute column positions in the input. However, you can also use signed numbers to indicate positions relative to the last pattern match. You can even use positional patterns to make REXX "back up" in the input string. 1.12.5. Parsing an Expression In addition to parsing input from the keyboard or a variable, PARSE can evaluate an expression and parse the resulting string. The syntax for this variant of PARSE is: PARSE [UPPER] VALUE expression WITH template A frequent source of expressions to parse are the REXX built-in functions, which we'll discuss in the next section. For now, here's a simple PARSE VALUE example that parses the result returned by the REXX function TIME(), which returns the current time in the format hh:mm:ss. The PARSE VALUE instruction below assigns hh to the variable hours, mm to minutes, and ss to seconds. Breaking up the value of the TIME() Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.28 ======================================================================== function in this way allows the SAY instruction to display the time in a simpler format. PARSE VALUE TIME() WITH hours ':' minutes ':' seconds SAY hours "Hours" minutes "Minutes" If your computer's clock thinks the time is 11:18am, the preceding two clauses would display 11 Hours 18 Minutes ________________________________________________________________________ Still More About Parsing: PARSE is probably REXX's most powerful instruction. While we've touched on most of the PARSE variants here, we haven't begun to explore them in depth. For more PARSE documentation and examples see The REXX Language, Section 10, "Parsing for ARG, PARSE, and PULL". ________________________________________________________________________ 1.13. Using Built-in Functions REXX is distinguished by its wealth of powerful built-in functions. The newcomer's best policy is to assume that just about any kind of data manipulation is already available within REXX. Before writing a low-level algorithm in REXX, refer to The REXX Language, Part 2, Section 9, "Built-in Functions" to see if REXX has already done the work for you. You can think of the built-in functions as falling into three groups: the string, conversion, and information functions. We'll discuss each of these groups separately. In a later section, you'll see how to define your own functions that work just like the REXX built-in functions. First, let's look at how to invoke functions. 1.13.1. Invoking Functions You can think of the REXX built-in functions as "black boxes" that perform a specialized function quietly and efficiently as long as you supply the arguments for them to work on, and the place to store or use the result they return. All REXX functions are named according to the rules for symbols, and are executed simply by issuing their name followed by a string of arguments enclosed in parentheses. Even if there are no arguments you must include the parentheses after the function name, and the parentheses must follow the function name directly -- with no intervening blank. Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.29 ======================================================================== (There is another way to invoke a REXX routine, using the CALL instruction, when you don't need the returned result. We'll discuss this instruction in the next section.) You might invoke the built-in REXX function DATE() as follows month=DATE("M") or SAY DATE("N") In the first example above DATE() is passed the argument "M" (for Month) telling the function to return the full name of the current month; and in the second example DATE() is passed "N" (for Normal) telling the function to return the date in the default format. In the first example the result received from DATE() is assigned to a REXX variable, month. In the second example the result is displayed on the screen with SAY. Note that your programs must always be written to do something with the value returned by a function. If nothing is done with a returned value, REXX will pass the returned expression to the environment as a command, which is usually not what you intend. 1.13.2. String Functions If you have worked with string functions in other languages, you may be surprised by the versatility of those built into REXX. REXX has many more string functions than most other languages, and the individual functions have extra arguments (with reasonable defaults) that significantly increase their power. As a small example, consider the REXX function STRIP(). STRIP can remove leading blanks from a string, or trailing blanks, or both. And it can also be told what character to remove; blanks are merely the default. Some of REXX's helpful string functions are: CENTER() center a string within a length COPIES() return concatenated copies of a string DELSTR() delete a substring from a string LEFT() return characters from left side of string LENGTH() return the length of string POS() find one string in another Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.30 ======================================================================== RIGHT() return characters from right side of string STRIP() remove leading/trailing characters from string SUBSTR() return substring from a string WORD() return nth blank-delimited word in string These are just the fundamental string functions. There are many more for performing complex tasks such as comparison, translation, and word manipulation. Here is a program that uses some of the string functions to display a banner: /* LEARN23.REX -- prompt user for title, display banner */ WIDTH=40 BORDER=4 BCHAR='*' SPACE=' ' SAY "Enter title: " PARSE PULL title IF LENGTH(title) > WIDTH-BORDER THEN SAY "Sorry, your title is too long" ELSE DO SAY COPIES(BCHAR, WIDTH) SAY BCHAR COPIES(SPACE, WIDTH-BORDER) BCHAR SAY BCHAR CENTER(title, WIDTH-BORDER) BCHAR SAY BCHAR COPIES(SPACE, WIDTH-BORDER) BCHAR SAY COPIES(BCHAR, WIDTH) END /* END LEARN23.REX */ If you run LEARN23.REX and enter a title such as REXX, you will get output like this: **************************************** * * * REXX * * * **************************************** Notice how the REXX functions COPIES() and CENTER(), along with the blank concatenation operator, make formatting text displays extremely easy. The string values returned by the functions are automatically concatenated with the BCHAR variable, and displayed by the SAY instruction. Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.31 ======================================================================== 1.13.3. Conversion Functions The built-in REXX conversion functions let you easily change the format, appearance, and number base of REXX data. Examples of helpful conversion functions include C2D() convert an ASCII character to its value in decimal C2X() convert an ASCII character to its value in hexadecimal D2C() convert a value in decimal to an ASCII character D2X() convert a value in decimal to a hexadecimal value FORMAT() round and format a number TRUNC() return the integer part of a number X2C() convert a value in hexadecimal to an ASCII character X2D() convert a value in hexadecimal to a decimal value Here is a program that uses some of the conversion functions: /* LEARN24.REX -- general-purpose number base conversions */ SAY "Enter value to convert:" PULL n /* convert from ASCII if only one character was entered */ IF LENGTH(n) = 1 THEN DO SAY '['n'] ASCII -> Decimal = ' C2d(n) SAY '['n'] ASCII -> Hex = ' C2x(n) END /* convert from decimal if valid number was entered */ IF DATATYPE(n,'N') THEN DO SAY '['n'] Decimal -> ASCII = ' D2C(n) SAY '['n'] Decimal -> Hex = ' D2x(n) END /* convert from hex if valid hex number was entered */ IF DATATYPE(n,'X') THEN DO SAY '['n'] Hex -> ASCII = ' X2C(n) SAY '['n'] Hex -> Decimal = ' X2d(n) END /* END LEARN24.REX */ If you run LEARN24.REX and enter the number 35 to convert, you will get these results: Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.32 ======================================================================== [35] Decimal -> ASCII = # [35] Decimal -> Hex = 23 [35] Hex -> ASCII = 5 [35] Hex -> Decimal = 53 1.13.4. Information Functions The built-in REXX information functions return values that tell you more about REXX's internal status, or the environment REXX is running in. Examples of helpful information functions include DATATYPE() detect/test REXX data types such as numeric, alphanumeric DATE() return local date in many different formats DIGITS() return the current significant digits used in arithmetic operations RANDOM() return a pseudo-random number TIME() return local time in many different formats Here is a program that uses the REXX built-in functions DATE() and TIME() to display the day and time in a readable format: /* LEARN25.REX -- show readable date and time */ PARSE VALUE DATE() WITH day month year SAY "It's" TIME("C") "on" DATE("W") DATE("M") day"," year"." /* END LEARN25.REX */ LEARN25.REX displays something similar to It's 12:50pm on Thursday August 18, 1988. 1.14. User-defined Subroutines and Functions If you use REXX frequently, or use it for large projects, you will soon want to define your own subroutines and functions. Subroutines and functions make understanding your programs easier by dividing them up into smaller functional units. And they make writing your programs easier by forming general-purpose algorithms that can be re-used in the same program, and in future programs. Before exploring the finer points of writing REXX subroutines and functions, let's look at a simple example: Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.33 ======================================================================== /* LEARN26.REX -- call subroutine and pass it an argument */ CALL FindArea 5 r = 50 CALL FindArea r EXIT FindArea: /* compute area of a circle from its radius */ ARG radius area = 3.141592 * (radius)**2 SAY "A circle with a radius of" radius "has an area of" area RETURN /* END LEARN26.REX */ When run, LEARN26.REX displays A circle with a radius of 5 has an area of 78.539800 A circle with a radius of 50 has an area of 7853.98000 1.14.1. Invoking Subroutines and Functions LEARN26.REX contains an example of a user-defined REXX subroutine FINDAREA. Unlike a function, a subroutine does not directly return a value to the user. You execute a REXX subroutine using the CALL instruction. The syntax for CALL is: CALL name [expression, expression, ... ] where name is the name of the subroutine, and the expressions are optional data arguments for the subroutine to use. Note that any arguments of a routine called via the CALL instruction are not enclosed in parentheses. ________________________________________________________________________ Searching For Routines: When you CALL a subroutine or reference a function, REXX searches first for a label defining the named routine in your program. If REXX doesn't find an internal label, then it searches next for a built-in function, and finally for an external routine. For the rules governing how Personal REXX searches for external routines, see Section "External Functions", "Invoking External REXX Functions". ________________________________________________________________________ Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.34 ======================================================================== 1.14.2. Defining Subroutines and Functions You designate the start of a REXX subroutine or function in your program with a label -- a REXX symbol followed by a colon (:). The body of a subroutine or function consists of normal REXX clauses. You designate the end of a subroutine or function with the RETURN instruction. The syntax for RETURN is: RETURN [expression] where expression is required for functions (which always return a value to their caller), but optional for subroutines. When RETURNing from a subroutine invoked with CALL, the special REXX variable RESULT is set to the value of expression.¶ The caller can (but need not) use the value stored in RESULT. When RETURNing from a function, the value of expression is used in the expression from which the function was invoked. 1.14.3. Subroutines Versus Functions Let's look at another version of the last LEARN program that demonstrates the differences between REXX subroutines and functions: /* LEARN27.REX -- have subroutine invoke function */ CALL FindArea 5 r = 50 CALL FindArea r EXIT FindArea: /* subroutine to display area of circle */ ARG radius area = CalcArea(radius) SAY "A circle with a radius of" radius "has an area of" area RETURN CalcArea: /* function to compute area of circle */ ARG radius RETURN 3.141592 * (radius)**2 /* END LEARN27.REX */ ========================= ¶ If no expression was specified, the value of RESULT does not change. Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.35 ======================================================================== This version behaves exactly as the first one did, but it moves the chore of computing the area of the circle into a separate REXX function. As you can see, there is no difference in the way you define a REXX subroutine or function. The only difference is in how you invoke the subroutine or function, and how you handle the result returned. A given REXX routine can be invoked as a subroutine or as a function. For example, CALL name expression1, expression2 or result = name(expression1, expression2) can both be used to invoke the routine NAME. Unfortunately, these two different syntaxes often cause confusion for newcomers to REXX. The thing to remember is that when using CALL to invoke a routine, you don't enclose the arguments of the routine in parentheses. When you invoke a routine as a function, the arguments must be enclosed in parentheses. 1.14.4. EXIT Instruction Notice that both LEARN26 and LEARN27 have a REXX EXIT instruction after the main body of the program and before the label that marks the beginning of the first user-defined routine. EXIT causes REXX to unconditionally terminate a program. The syntax for EXIT is: EXIT [expression] where expression is used to set a return code that the program's caller can examine. If the LEARN26 and LEARN27 did not contain their EXIT instructions, then execution would pass from the end of the main program into the user-defined routines. The results would be unexpected, and probably unpleasant! 1.14.5. Arguments LEARN26 and LEARN27 demonstrate how to pass an argument to a routine and how to retrieve it from within the routine. You pass arguments simply by placing them after the routine's name, with each argument separated from the next by commas. The easiest way to retrieve arguments is to use REXX's PARSE ARG instruction. The syntax for PARSE ARG is: PARSE [UPPER] ARG template Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.36 ======================================================================== This PARSE instruction works just like the other PARSE variants you've seen except that the data assigned to the template comes from arguments passed to the routine. LEARN26 and LEARN27 use the REXX instruction ARG, which is a shorter form of PARSE UPPER ARG. The syntax for ARG is just: ARG template The routines in the LEARN programs above receive only one argument and thus use simple templates of only one variable. If more than one argument is being passed to a routine, then each argument can be accessed in turn by placing commas in the template. ________________________________________________________________________ ARG() Built-in Function: REXX also offers the ARG() built-in function in addition to the PARSE ARG instruction. ARG() can tell you how many arguments were passed, test for the existence of a specific argument, and return the value of any argument. ________________________________________________________________________ /* LEARN28.REX -- demonstrate REXX argument passing */ CALL Sum 1, 2 /* call as subroutine */ SAY "RESULT="RESULT /* special variable */ our_result = Sum(3, 4) /* call as function */ SAY "our_result="our_result EXIT Sum: ARG first, second SAY "Sum:" SAY " Arguments passed=" Arg() SAY " first =" first"," Arg(1) SAY " second=" second"," Arg(2) RETURN first + second /* return sum of arguments */ /* END LEARN28.REX */ Running LEARN28.REX yields these results: Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.37 ======================================================================== Sum: Arguments passed= 2 first = 1, 1 second= 2, 2 RESULT=3 Sum: Arguments passed= 2 first = 3, 3 second= 4, 4 our_result=7 1.14.6. Variable Scope When a REXX subroutine or function receives arguments, are they the "real" thing from the main program, or just copies? When a routine assigns values to arguments or variables, what happens to variables of the same name in the main program? By default, a main routine and all subroutines and functions in the same source file share a single pool of variables. If an argument or variable has the same name as a variable in the calling routine, then it is that variable. However, by placing the PROCEDURE instruction after the label that defines a subroutine or function, you set up a local variables environment within a routine that is completely insulated from the rest of the program. The EXPOSE option on PROCEDURE can then be used to selectively make outside variables available to a routine. The syntax for PROCEDURE is: PROCEDURE [EXPOSE variablelist] If used, PROCEDURE must be the first instruction after the routine's label. Multiple variables in the EXPOSE variablelist are not separated by commas. The original variable environment of the calling routine is restored upon RETURNing from a called routine that used the PROCEDURE instruction. Any changes made within the called routine to EXPOSEd variables persist, but other variables used in the called routine become unassigned. Here's an example of how REXX's variable scoping works: Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.38 ======================================================================== /* LEARN29.REX -- demonstrate REXX variable scoping */ a=1 SAY "Main (j unassigned):" CALL PrintVars CALL Proc1 a SAY "Main (j created, a changed):" CALL PrintVars CALL Proc2 SAY "Main (a changed, j restored):" CALL PrintVars EXIT Proc1: /* everything is accessible */ ARG j SAY "Proc1 (j created equal to a):" CALL PrintVars a = 3 RETURN Proc2: PROCEDURE EXPOSE a /* a from main is accessible, j is local copy */ SAY "Proc2 (a changed, j changed locally):" a = 7; j = 10 SAY "a="a "j="j RETURN PrintVars: SAY "a="a "j="j RETURN /* END LEARN29.REX */ The results from running LEARN29.REX look like this: Main (j unassigned): a=1 j=J Proc1 (j created equal to a): a=1 j=1 Main (j created, a changed): a=3 j=1 Proc2 (a changed, j changed locally): a=7 j=10 Main (a changed, j restored): a=7 j=1 Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.39 ======================================================================== 1.15. Commands to External Environments REXX was designed in part as a macro or command language. For this reason, REXX programs have the ability to easily pass commands to external environments for execution. In theory, any program that has a command language interface could be programmed by its developer to interact with REXX. PC command environments that work with include the DOS command processor, Quercus Systems' REXXTERM communication package, and the KEDIT text editor from Mansfield Software Group. In this section we will see how to pass commands from REXX to DOS, the default active environment when Personal REXX is executed. Issuing Commands The way you tell REXX to send a command to another environment is very simple. Here is a REXX program that consists entirely of commands to DOS: /* LEARN30.REX -- display DOS information using DOS commands */ 'ver' /* DOS command to display DOS version */ 'path' /* DOS command to display DOS path */ 'vol' /* DOS command to display current volume name */ /* END LEARN30.REX */ Any time REXX sees a clause that consists only of an expression, that expression is evaluated and the resulting string is sent to the active environment as a command. Thus, "commands" are simply character strings that have no intrinsic meaning to REXX. (Notice that in LEARN30 each DOS command is enclosed in quotes. Although this is not strictly necessary in this example program, it is a good idea always to enclose in quotes command names that you want to be taken literally.) When an environment returns control to REXX, it sets an integer return code that REXX then places in the special variable RC. Your program can test the RC variable and take some action based on its value. For programs running under DOS, a non-zero return code indicates an error. (However, many DOS commands always have a return code of zero, whether they succeed or not.) ________________________________________________________________________ How DOS Processes Commands: When Personal REXX passes commands to the DOS environment, there are several ways the command could be handled. DOS commands can be internal to COMMAND.COM, or external files in any of the directories in the current DOS PATH. For more detail on how DOS Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.40 ======================================================================== will handle commands you issue, see Section "DOS Commands", "Executing DOS Commands", in this User's Guide. ________________________________________________________________________ Let's look at a more involved example of using REXX to issue commands to the DOS environment. The LEARN31.REX program reads an ASCII file that contains a list of file names and applies a DOS command (such as DEL or COPY) to each file named. This program would be handy if you wanted to perform a DOS operation on a set of files that couldn't easily be matched with the DOS wildcard characters. (LEARN31.REX uses the REXX file I/O functions, which will be covered thoroughly in a following section.) /* LEARN31.REX -- execute DOS command against list of files */ filelist = 'files.lst' ARG command /* get user's command */ DO WHILE Lines(filelist) > 0 /* loop through file list */ current_fileid = LineIn(filelist) /* get a filename */ command current_fileid /* execute command against it */ END CALL LineOut filelist /* close file list */ /* END LEARN31.REX */ LEARN31.REX contains the first example we've seen of using the ARG instruction to access arguments to a program. Arguments to a program are contained in a single string that the user has typed on the command line, and they behave just like arguments to a routine. Note that REXX treats arguments to a program as one string, so you do not use commas in the PARSE ARG template to access different words in the arguments to a program. Suppose that you run LEARN31.REX with TYPE as the argument: rexx learn31 type and that you have a file called FILES.LST in your current directory that contains the lines: a:chapter1.doc a:chapter2.doc a:chapter3.doc a:chapter4.doc In this case the variable command is assigned the value TYPE. In the DO loop, the assignment statement Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.41 ======================================================================== current_fileid = linein('files.lst') uses the built-in function LINEIN to read the next line from the file FILES.LST and assign it to the variable current fileid. Then a DOS command is constructed using the variables command and current fileid. The first time through the loop in our example the command that is constructed is TYPE a:chapter1.doc The body of the DO WHILE loop then keeps executing as long as there are lines remaining to be read in FILES.LST. When the loop is left, the CALL instruction calls the built-in function LINEOUT to close the file FILES.LST. (All files that are opened by a REXX program should be closed before exiting the REXX program.) ________________________________________________________________________ What is a Command, What is REXX?: Newcomers to REXX, especially those using REXX as a command language, may be puzzled about what is processed by REXX, and what is passed to the environment for processing. Keep in mind that any clause that consists only of an expression is considered a command to the environment. This means that REXX will send to the active environment any clause that it doesn't recognize as an instruction or assignment. ________________________________________________________________________ 1.16. Advanced Features of REXX In this section we'll cover a selection of more-advanced REXX features that you may find helpful: numeric operations, debugging, and the INTERPRET instruction. 1.16.1. Numerics and Math REXX is unique in that its mathematical operations are defined so that different implementations of the language on different hardware architectures will yield identical results. The REXX mathematical operations are defined such that no errors are introduced into calculations except those resulting from the final rounding to the precision you have requested. Thus, unlike other languages that may accumulate "round-off" errors that insidiously corrupt your results, REXX puts the accuracy of its mathematical operations entirely in your control. The price paid for this accuracy is slower calculation speed, though you aren't likely to notice much overhead in the vast majority of applications. Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.42 ======================================================================== 1.16.2. NUMERIC Instruction The REXX instruction NUMERIC controls the way in which REXX carries out arithmetic operations. The most commonly used form of the NUMERIC instruction is: NUMERIC DIGITS expression NUMERIC DIGITS controls the precision to which REXX computes arithmetic expressions. Numeric results will be rounded to the number of digits you specify in expression. The default is nine digits. Lower values are likely to give unreliable results due to round-off error, whereas higher values will require more processing time. /* LEARN32.REX -- demonstrate REXX numeric operations */ NUMERIC DIGITS 30 SAY 5/3 /* writes 1.66666666666666666666666666667 */ NUMERIC DIGITS 10 SAY 5/3 /* writes 1.666666667 */ /* END LEARN32.REX */ ________________________________________________________________________ FORMAT() Built-in Function: If the results provided by REXX's current NUMERIC settings don't appear the way you want, then you can use the built-in FORMAT() function to round and display them in a different format. You can also use FORMAT() to convert results to or from exponential form. Comparing REXX Numbers You can use any of REXX's comparison operators for comparing numeric values. However, if you use the strict comparison operators (the ones with doubled characters), then you are really comparing the string representations of the numbers. This means that leading and trailing blanks and zeros will be significant. Usually you will want to use the normal comparison operators to compare REXX numbers. ________________________________________________________________________ 1.16.3. Debugging with the TRACE Instruction One of the unique features that makes REXX particularly easy to use is its built-in support for that bleak activity that can consume much of a programmer's time: debugging. REXX offers this support via the TRACE instruction. TRACE gives you the same information you may have obtained in the past by embedding print statements in your programs and making other modifications to them. But TRACE tells you about program execution much Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.43 ======================================================================== more efficiently, and without you having to add debugging statements to your program. The TRACE instruction sets a tracesetting that controls which clauses REXX will display diagnostic information for during execution. The tracesetting can be set to many different levels of sensitivity: no clauses at all, clauses that generate certain types of errors, or all clauses. (The default tracesetting causes nothing but host commands that result in failure to be traced.) The arguments and output for TRACE are more terse than for the other REXX instructions, because users frequently must work with TRACE manually during interactive debugging sessions. Taking a few minutes now to understand TRACE will quickly be repaid the next time one of your REXX programs doesn't behave as expected. The syntax for TRACE is: TRACE tracesetting The most often used trace settings are N (the default) and R. N causes TRACE to show only failed host commands. R causes TRACE to show each clause of the traced program before execution and the values of any expressions in that clause. You can embed TRACE instructions in your program text, or you can type them in directly to REXX once REXX has begun tracing interactively. The question mark (?) prefix on any alphabetic tracesetting toggles interactive tracing on and off. When REXX is tracing interactively, further TRACE instructions within the program are ignored and execution pauses after most clauses. Once execution has paused you can either enter a blank line to continue execution or type in any clause to be executed by REXX. Clauses you enter will affect your program just as if they were embedded at the point where execution paused. Usually you will just enter SAY instructions to see the values of variables, or TRACE instructions to change the tracesetting. ________________________________________________________________________ Selective Tracing Through Routines: The tracesetting is automatically saved and restored during subroutine calls, which means that you can selectively trace a subroutine without affecting the trace status in the rest of the program. ________________________________________________________________________ All the traced clauses and results are displayed on screen with a three-character prefix. A list of these prefixes and the other tracesettings may be found in the discussion of TRACE in The REXX Language, Part 2, Section 7, "Keyword Instructions". More information Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.44 ======================================================================== on tracing with Personal REXX can be found in Chapter 7, "Tracing and Interactive Debugging" of this User's Guide. Let's look at a quick example of using TRACE to debug a program: /* LEARN33.REX -- use TRACE to find bug */ /* TRACE ?R */ SAY Sum(1, 2) EXIT Sum: ARG i j RETURN i + j /* END LEARN33.REX */ At first glance, LEARN33.REX looks like a reasonable attempt to sum two numbers with a user-defined function SUM(). However, there is a subtle problem that causes REXX to report an error: 11 +++ RETURN i + j 5 +++ SAY Sum(1, 2) Error 41 on line 11 of LEARN33.REX: Bad arithmetic conversion Unfortunately, the two clauses REXX has reported back to us are syntactically correct. The problem lies somewhere else, and could be difficult to spot without TRACE. Using a text editor, remove the comment delimiters from /* TRACE ?R */ so that the line now reads TRACE ?R Then try running LEARN33.REX again. What you'll see displayed is: 5 *-* SAY Sum(1, 2) +++ Interactive trace. 'Trace off' to end debug. ? The interactive TRACE shows the initial SAY instruction and the call to the SUM() function. There are no problems here, so press Enter at the question mark to continue execution. What is then displayed is: 9 *-* Sum: ? Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.45 ======================================================================== This is the label for the internal function SUM(). Now hit Enter again. TRACE shows the ARG instruction within SUM(), and the values it assigns to the variables i and j. Here's a problem! 10 *-* ARG i j >>> "1" >>> "" ? Only the 1 was assigned to i; the 2 disappeared somehow and was not assigned to j. To confirm this, enter a SAY instruction at the question mark to see the value of j: SAY "j="j What's displayed is j= ? indicating that in fact j has a null string as its value. At this point you can temporarily "fix up" the program, if you like, by typing j=2 ? Now hit Enter and the program continues to run clause-by-clause as you hit Enter. To leave the program at any point, type exit at the question mark. If you then look up the ARG instruction in The REXX Language, you'll find that a comma () must be used in a parsing template if you want more than one argument string to be available to a routine. Edit LEARN33.REX again, this time changing the clause ARG i j to read ARG i, j Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.46 ======================================================================== Run the program again. REXX displays 3 on the screen, as it should. 1.16.4. INTERPRET Instruction The REXX INTERPRET instruction executes clauses that have been built dynamically while a program is running. With INTERPRET it's as if your REXX program had the ability to create its own program, and then execute it. This can be very powerful for handling certain specialized situations. The syntax for INTERPRET is: INTERPRET expression The expression is evaluated, then it is executed exactly as if the evaluated expression had existed in the program text. Here is a simple application of INTERPRET that gives a user the full power of REXX's expression evaluator in a handy program that acts like a pocket calculator: /* LEARN34.REX -- calculate result from any REXX expression */ ARG calculation INTERPRET "SAY" calculation /* END LEARN34.REX */ You can use LEARN34.REX to calculate the value of a complex expression. For example, suppose you execute LEARN34.REX with Personal REXX by typing rexx learn34 15 + 3.14 * (64.9876/32.89867)**3 The program then displays this result: 39.2039431 Another example of INTERPRET can be found in RXTRY.REX in the \ SAMPLES directory of the distribution disk. You'll probably want to try out RXTRY. After RXTRY starts up, it waits for you to enter a REXX clause and then interprets that clause for you. RXTRY continues to interpret clauses you enter; entering EXIT terminates RXTRY. Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.47 ======================================================================== 1.17. Personal REXX File Input and Output To be most useful to you, this section describes REXX I/O as it is implemented in Personal REXX. I/O operations are necessarily system dependent, so each REXX implementation has to flesh out the I/O framework provided by The REXX Language. The DOS file system forces some minor limitations on the Personal REXX I/O functions. In general, Personal REXX programs can be very portable to other REXX environments. For a complete description of the generic REXX I/O framework, see The REXX Language, Part 2 Section 12, "Input and Output Streams". For more information on the Personal REXX I/O functions see Chapter 6, "Input and Output in Personal REXX" in this User's Guide. 1.17.1. Default I/O Streams REXX assumes a "default character input stream" and a "default character output stream". Usually the default input stream is the user's keyboard and the default output stream is the screen. The fundamental instructions for input and output in REXX -- PULL and SAY -- use the default character streams automatically. PULL takes input from the default character input stream, and SAY sends output to the default character output stream. The Personal REXX instructions SAY and PULL work exactly as described in The REXX Language. The general-purpose REXX file I/O routines discussed below work with any character stream, where you specify the name of a specific stream to use. This might be the name of a disk file, a printer, or the user's keyboard and screen. 1.17.2. I/O Routines REXX allows for character-by-character or line-by-line manipulation of files. The facilities for file input and output in REXX are quite simple, requiring you to know only a handful of built-in routines. To work with character streams other than the default (such as DOS files), you use the REXX built-in LINEIN(), LINEOUT(), LINES(), CHARIN(), CHAROUT(), and CHARS() routines.ý All of these routines take as their first argument the name of the file to be used. If that name is omitted, then the routines use the default character I/O streams. ========================= ý If you will be using CMS REXX as well as , please note that as of VM/SP Release 6 CMS REXX does not include the built-in I/O functions discussed here and detailed in The REXX Language. (For compatibility between CMS REXX and , you can use the less efficient EXECIO command discussed in Chapter 10, "LISTFILE, EXECIO, and GLOBALV".) Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.48 ======================================================================== Let's look at a simple example of REXX file I/O that shows off the power and ease-of-use of REXX's I/O routines: /* LEARN35.REX -- write time-stamped note to disk file */ PARSE ARG newline CALL LineOut "notes.doc", Date() Time() newline CALL LineOut "notes.doc" /* close file */ /* END LEARN35.REX */ The first clause of this program receives an argument, the text of a one-line "note", from the user's command line. The second clause uses built-in REXX functions to find the current date and time, then appends them along with the user's note to the bottom of the NOTES.DOC file. The last clause closes the file. For example, you might run LEARN35.REX by entering rexx learn35 Pick up suit at cleaners Line I/O Routines REXX's LINEIN(), LINEOUT(), and LINES() routines are meant for handling files with data stored as lines. For DOS this means standard ASCII files, where lines of text end with the carriage return-linefeed character sequence. With , the line I/O routines will give unreliable results if used on files that aren't in standard ASCII format. These are the REXX line I/O routines: LINEIN([file]) Reads one line from file. LINEOUT([file], [string]) Writes string to file, followed by a carriage return-linefeed sequence. If string is not specified, the file is closed. LINES([file]) Tests if end-of-file has been reached, returning 1 if any lines remain to be read from file, or 0 if no lines remain or an end-of-file character has been found. ________________________________________________________________________ Parsing File Input Directly: If you want to PARSE a line of data from a file immediately upon reading it, rather than storing it in an intermediate variable and then using PARSE VAR, you can use an expression like: Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.49 ======================================================================== PARSE VALUE LineIn(file) WITH template For example, PARSE VALUE LineIn('notes.doc') WITH notedate notetime notetext ________________________________________________________________________ Opening and Closing Files In Personal REXX you never need to explicitly open a file. The first time you use a file, Personal REXX opens it for you automatically. If you write to an existing file, Personal REXX will normally append what you write to the end of the file. If you want your output to replace the existing file, you need to use the Personal REXX function DOSDEL() to delete the file first. You should always close a Personal REXX file when you are finished with it. You do this by calling the LINEOUT() or CHAROUT() routines, giving only the name of the file as an argument. You can see an example of this in the last line of LEARN35.REX above. Let's look at another file I/O program in Personal REXX: /* LEARN36.REX -- copy one ASCII file to another */ PARSE ARG inputname outputname /* delete old output file so we don't append to it */ CALL DOSDel outputname DO WHILE Lines(inputname) > 0 /* check for end-of-file */ CALL LineOut outputname, LineIn(inputname) /* write line */ END CALL LineOut inputname /* close input file */ CALL LineOut outputname /* close output file */ /* END LEARN36.REX */ This program is invoked with a command line such as learn36 notes.doc notes.old It calls the Personal REXX function DOSDEL() to delete the output file. If the output file existed on disk when the copying began, then the input file would be copied to the end of the file, rather than replacing it. Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.50 ======================================================================== Next, the program enters a DO loop that reads a line from inputname with LINEIN() and writes that line to outputname with LINEOUT(). The loop terminates when LINES(inputname) is equal to 0, meaning there are no more lines in the inputname file. Finally, the program closes the files it has used by calling LINEOUT() with the file names only as arguments. File Names You need to be consistent when you pass a file name to Personal REXX. If you specify a file differently from one call to the next -- for example with a path prefix one time, and without the path prefix the next time -- Personal REXX may not realize you are referring to the same file. The easiest way to ensure consistency is to assign filenames to a variable, and then always use that variable to refer to the file. DOS systems have hardware devices with names that you can treat just like file names. Two of these -- CON for the console or screen, and PRN for the default system printer -- are especially handy. For example, if you enter CON for the output file in LEARN36.REX, the input file will be listed on your screen. If you enter PRN for the output file, the input file will be printed. Let's look at a final application of REXX's line I/O routines to search for character strings within a file: /* LEARN37.REX -- list occurrences of string in file */ PARSE ARG fileid string DO WHILE Lines(fileid) > 0 newline = LineIn(fileid) /* read line */ IF Pos(string,newline) \= 0 THEN DO /* search for string */ SAY newline found = "yes" END END CALL LineOut fileid /* close the file */ IF found \= "yes" THEN SAY "String" string "was not found in" fileid /* END LEARN37.REX */ LEARN37.REX receives an input file name and a search string on the command line. For example, rexx learn37 c:\ autoexec.bat rxintmgr Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.51 ======================================================================== The program loops through the file line by line, using the REXX built-in function POS() to determine if the string is contained within a given line. If so, that line is displayed and a flag called found is set. When the program is done looping, it closes the file, then checks the flag to see if any strings were found. Character I/O Routines The REXX routines CHARIN(), CHAROUT(), and CHARS() can be used to randomly access positions in any DOS file since they treat files as continuous streams of characters rather than as lines of ASCII text. Unlike the line I/O routines, the character I/O routines do not treat carriage return, linefeed, or end-of-file characters specially. REXX always remembers the current read and write position in a file. If a start position is not specified in a call to the CHARIN() or CHAROUT() routines, then REXX reads or writes the file at the currently remembered position. These are the REXX character I/O routines: CHARIN([file], [start], [length]) Reads one or more characters from file at position start. If length is not specified, then one character is read. CHAROUT([file], [string], [start]) Writes string to file. If neither string nor start are specified, then the file is closed. If file is omitted from a call to CHAROUT(), then the string is sent to the display. Unlike with the SAY instruction, a carriage return-linefeed pair is not appended to the written string. Thus the cursor remains on the same line, immediately following the string. You will probably use CHAROUT() most often for this ability to write strings on the screen without advancing the cursor to the next line. CHARS([file]) Tests if the end-of-file has been reached, returning 1 if any characters remain to be read from file, or 0 if no characters remain. CHARS() is not sensitive to the ASCII end-of-file character. Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.52 ======================================================================== 1.18. Personal REXX Utility Functions Personal REXX includes a set of built-in utility functions that help you take full advantage of the IBM PC and DOS. Although these functions have the "flavor" of REXX -- being high-level, easy to use, and replete with options -- they are not a part of the standard REXX language. Any programs that rely on these functions will have to be modified to run in another environment. We won't look at all of the Personal REXX utility functions here. However, we'll discuss the different function groups: Hardware Information, Hardware Access, Miscellaneous, and DOS, and give example programs that show how to use some of the functions from each group. For complete information on all the Personal REXX utility functions see Chapter 8, "Personal REXX Utility Functions". 1.18.1. Hardware Information Functions The functions in the Personal REXX Hardware Information group return information about the IBM PC hardware. The LEARN38.REX program uses the several hardware information functions to find out about the host PC. /* LEARN38.REX -- analyze host PC */ SAY "ROM Date :" PCROMDate() SAY "DOS Version:" DOSVersion() SAY "" SAY "RAM:" PCRAM() SAY "EMS:" EMSMem() SAY "" SAY "DRIVE :" DOSDrive() SAY "DIRECTORY:" DOSCD() /* END LEARN38.REX */ The actual results that this program displays are dependent on your particular PC. The results will be something like this: ROM Date : 02/13/87 DOS Version: 3.30 RAM: 640 EMS: 950272 1048576 DRIVE : C DIRECTORY: \PROGDIR Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.53 ======================================================================== 1.18.2. Hardware Access Functions The SAY and PULL instructions allow your REXX programs to write to the screen and read from the keyboard, but these instructions do not let you control such things as the cursor position and color of displayed text. If you want to write programs that take more direct advantage of the PC's video and keyboard hardware, you will rely on the Personal REXX Hardware Access functions. The functions in the Hardware Access group control the cursor, read keystrokes from the PC keyboard, access memory and I/O ports directly, and write character strings and video attributes to the screen. Here is a program that uses the Hardware Access functions to initialize the screen and then wait for the user: Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.54 ======================================================================== /* LEARN39.REX -- draw border, wait for user to hit key */ CALL ScrClear /* clear the screen */ CALL Cursor 3,5 /* cursor in row 3 column 5 */ CALL Frame 1,1,25,80 /* draw the frame */ CALL InKey /* wait for user to hit key */ CALL ScrClear /* clear the screen */ EXIT Frame: PROCEDURE /* draw border with IBM double-line extended characters */ ARG urow, ucol, lrow, lcol /* upper left & lower right */ /* coordinates of frame */ horiz = D2C(205) /* ASCII 205: horizontal border */ vert = D2C(186) /* ASCII 186: vertical border */ tleft = D2C(201) /* ASCII 201: top left corner */ tright = D2C(187) /* ASCII 187: top right corner */ bleft = D2C(200) /* ASCII 200: bottom left corner */ bright = D2C(188) /* ASCII 188: bottom right corner */ width = lcol - ucol - 1 /* width of horizontal border */ hcol = ucol + 1 /* start of horizontal border */ /* draw top of frame */ CALL ScrWrite urow, ucol, tleft CALL ScrWrite urow, hcol,, width, horiz CALL ScrWrite urow, lcol, tright /* draw sides of frame */ DO row = urow+1 TO lrow-1 CALL ScrWrite row, ucol, vert CALL ScrWrite row, lcol, vert END /* draw bottom of frame */ CALL ScrWrite lrow, ucol, bleft CALL ScrWrite lrow, hcol,, width, horiz CALL ScrWrite lrow, lcol, bright RETURN /* END LEARN39.REX */ 1.18.3. Miscellaneous Functions The Personal REXX Miscellaneous function group contains a collection of unrelated functions that convert dates, test memory, query the REXX environment, parse filenames, and manipulate strings. Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.55 ======================================================================== Here is a simple program that illustrates the PARSEFN() function for breaking up a file identifier into its component parts, and the UPPER() and LOWER() functions for changing the case of strings: /* LEARN40.REX -- process file name using miscellaneous functions */ filename = "c:\dos\CHKDSK.COM" parsedname = ParseFn(filename) SAY "Drive = " Upper(Word(parsedname, 1)) SAY "Path = " Upper(Word(parsedname, 2)) SAY "Base = " Lower(Word(parsedname, 3)) SAY "Exten = " Lower(Word(parsedname, 4)) /* END LEARN40.REX */ When run, this program displays Drive = C Path = \DOS\ Base = chkdsk Exten = com 1.18.4. DOS Functions The functions in the Personal REXX DOS group give you many of the same file-handling capabilities as DOS's COMMAND.COM shell, including changing drives and directories, modifying file attributes, deleting files, accessing the environment, making and removing directories, and renaming files. Some of the reasons you'd want to use a REXX function instead of the equivalent DOS command are: * With the REXX function you get an indication of success or failure, which is not available from many DOS commands. * You can avoid displaying DOS error messages that you can't normally suppress. * It's more efficient to call a built-in function than to issue a DOS command, since issuing a DOS command from a REXX program involves invoking COMMAND.COM. Here is a program that uses DOSDIR() and some other REXX features to automatically find all the executable files in the current directory, display a numbered menu of the found files, prompt the user for a file to run, then execute the user's choice: Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.56 ======================================================================== /* LEARN41.REX -- create menu of executable files and run one */ NULL = "" top = 1 CALL BuildList "*.exe" /* find files */ CALL BuildList "*.com" IF top = 1 THEN DO SAY "No executable files in current directory" EXIT END DO i=1 TO top-1 /* display menu */ SAY Format(i,2)":" Left(dirlist.i, Length(dirlist.i)-4) END CALL CharOut , D2C(10)"Enter choice (Hit ENTER to leave):" PULL choice /* get user's choice */ IF choice <> NULL THEN dirlist.choice /* issue command */ EXIT BuildList: PROCEDURE EXPOSE NULL dirlist. top ARG filespec dirlist.top = DOSDir(filespec, "n") DO WHILE dirlist.top <> NULL top = top + 1 dirlist.top = DOSDir(, "n") END RETURN /* END LEARN41.REX */ You run LEARN41.REX by entering rexx learn41 If the current directory contains executable files, then LEARN41 will display a menu something like this: 1: LIFE 2: NUMLOCK 3: CLEANFF 4: QS 5: DMAP 6: EMAP Enter choice: _ Personal REXX User's Guide, Version 3.0 1. Learning REXX page 1.57 ======================================================================== The menu lists the names of all the .EXE and .COM files found in the current directory. After the user types in a number and hits the Enter key, the appropriate program is executed. 1.19. What Next? Now that you've worked through the examples in this chapter, you should be able to write some useful REXX programs. But we've only touched on the many features of REXX here. Be sure to go through The REXX Language (especially Part 2) for more on REXX in general, and to take a look at the other chapters of this User's Guide (particularly Chapter 8, "Personal REXX Utility Functions") for more on . Personal REXX User's Guide, Version 3.0 2. REXX Language Compatibility page 2.1 ======================================================================== Chapter 2 REXX Language Compatibility ___________________________ There are several commonly used references for the REXX language. Cowlishaw's The REXX Language (2nd Edition) is supplied with . The IBM manual System Product Interpreter Reference (SC24-5239) describes the VM/CMS version of REXX. The IBM manual Procedures Language 2/REXX (S01F-0271) describes the IBM OS/2 version. The IBM manual Common Programming Interface Procedures Language Reference (SC26-4358) gives the IBM SAA specification for the language. The versions of the REXX language described in these references are very similar, making it possible for to be highly compatible with all of them. The instructions and functions added in version 4.0 of REXX¶ (the version described in The REXX Language, 2nd Edition) have been implemented in . The instructions and functions present in the CMS, OS/2, and SAA versions of REXX, but not included in The REXX Language, have also been implemented. Whenever portability is of concern and you must use features of that are different from corresponding features in other implementations or that are not available in other implementations, you can use the PARSE VERSION instruction to verify that the program is running with . For example, PARSE VERSION name version date will set name to "REXX/Personal", version to "4.0" (the language version, not the version), and date to the date of the current release of the language processor. There are some obvious ways in which REXX programs may be incompatible from one environment to the next. Some of the most common ways include: * System command names differ from one system to the next. This also applies to the names of available command environments and the default environment. * File naming conventions differ across file systems. * Most REXX implementations add built-in functions (always with different names) for working with the file system and other system features. ========================= ¶ Versions of the REXX language, such as 4.0, are currently assigned numbers by IBM. These version numbers are not directly related to version numbers of and should not be confused with them. Personal REXX User's Guide, Version 3.0 2. REXX Language Compatibility page 2.2 ======================================================================== * Many low-level details of how the I/O functions work are different, in particular how a file is regarded as a sequence of bytes or of lines. * Implementation limits such as the maximum length of a line, symbol, or string differ. * The underlying character set of the computer may be ASCII or EBCDIC. This affects binary and hexadecimal representations of characters and the standard collating sequence. * Binary data (i. e. numbers) are usually stored differently on different computers. It's clearly impossible to list all of the differences of from each of the other common implementations of REXX. The best this chapter can do is to point out the above general considerations and to give some important special cases. 2.1. Implementation Limits You will probably not be affected by any of the following limits except perhaps those on the size of programs and on space available for variable storage. Most REXX programs don't approach the other limits. * Size of a REXX program: varies greatly depending on your available memory and how you set 's environment variables. Under the right conditions, REXX programs with up to about 64K in each source file can be run. * Total size of all variables: varies greatly depending on your available memory and how you set 's environment variables. can use up to 64K of EMS memory per program as well as available DOS memory below 640K to store each source program's variables and the control blocks used to keep track of them. * Maximum number of open files: 15 * Maximum length of a single line of a program: 250 characters * Maximum length of a variable name: 250 characters * Maximum length of a symbol (before substitution): 250 characters * Maximum length of a literal string: 250 characters * Maximum length of a single "clause" (tokenized internal format): 1000 characters * Maximum number of tokens per clause: 250 Personal REXX User's Guide, Version 3.0 2. REXX Language Compatibility page 2.3 ======================================================================== * Maximum length of the value of a variable: theoretically 32000 characters, although it will be less in practice because of memory limitations and memory fragmentation. * Total static nesting of control structures (IF, SELECT, DO, etc.): 25 2.2. Differences from IBM OS/2 REXX This section lists the known incompatibilities between and IBM's OS/2 implementation of REXX (as of OS/2 1.3). * File read and write pointers as used by CHARIN, CHAROUT, LINEIN, and LINEOUT are maintained independently in , as required by The REXX Language. In IBM's OS/2 REXX the pointers are not independent, and both are updated whenever either a read or a write is done. * PULL and PARSE PULL display a prompt (currently "?") before reading keyboard input. does not. * IBM OS/2 REXX displays all commands passed to the system for execution, unless the ECHO OFF command has first been issued. displays commands only if the TRACE C instruction is used. * IBM OS/2 REXX does not allow REXX programs to be executed as commands unless they are invoked with the system CALL command. automatically runs REXX programs as commands. 2.3. Differences from CMS REXX This section lists the known incompatibilities between and the CMS implementation of REXX (as of VM/SP Release 6). * In CMS REXX, quoted strings are allowed to cross line boundaries. The REXX Language, however, specifies that quoted strings must fit on one line, and enforces this rule. * Since ASCII does not have a character for logical negation, several alternatives are provided in . Wherever a logical not sign is allowed in CMS REXX, either a backslash (\), a tilde (~), or a caret (^) may be used in . A forward slash (/) is sometimes used in CMS REXX for negation, so accepts it in the same cases, but its use is not recommended. Backslash (\) is the preferred character. * A program does not need to have a comment as its first line. (In certain contexts, such as in a KEDIT macro written in REXX, the comment line is required.) * The USERID built-in function returns the value of the USERID environment value, or the null string if USERID is not found in the environment. Personal REXX User's Guide, Version 3.0 2. REXX Language Compatibility page 2.4 ======================================================================== * The EXTERNALS built-in function always returns 0. * CMS REXX includes several options and functions related to Double Byte Character Set support, which are useful for working with languages like Japanese. These are not supported by . 2.4. Differences from REXX 4.0 This section lists the known incompatibilities between the current version of and version 4.0 of the REXX language specification, as described in The REXX Language, 2nd Edition. * The line and count arguments of the LINEIN function are not generally supported. LINEIN always attempts to read one line starting with the current read position in the specified character input stream. * The line argument of the LINEOUT function is not generally supported. LINEOUT always attempts to write a line at the current write position of the specified character output stream. * The LINES function returns either 1 or 0, depending on whether or not any lines remain in a file beyond the current read pointer. The CHARS function does return the number of characters remaining, unless used with a device or the standard input stream. * Trace output is not indented according to the logical level of nesting. * The special characters @, #, and $ are allowed in symbol names by for compatibility with earlier versions of the language. * Certain language features available in CMS REXX are now considered obsolete but are supported by for compatibility. The list includes the UPPER command, the C (Century) and J (Julian) options of the DATE function, and the EXTERNAL option of the PARSE instruction. * Likewise, various CMS built-in functions are still supported for compatibility. This includes EXTERNALS, FIND, INDEX, JUSTIFY, LINESIZE, and USERID. 2.5. Differences in Source Program Handling As discussed in Section "REXX Object Code", before running REXX programs first converts the programs to a more efficient internal form known as REXX object code. This leads to some slight differences from other REXX implementations: * Syntax errors in REXX programs that go undetected in other REXX implementations may be detected by . Other REXX implementations Personal REXX User's Guide, Version 3.0 2. REXX Language Compatibility page 2.5 ======================================================================== may miss syntax errors in portions of programs that, because of the flow-of-control, are not actually executed. checks all portions of REXX programs for syntax errors before execution begins. * programs are checked for syntax errors while being converted to internal form. Most conditions that would normally be handled by SIGNAL ON SYNTAX are instead caught during the conversion, and the program never begins execution. * TRACE S is not supported by , and since programs are always scanned for syntax errors before they are run, is not necessary. * Certain statements which do not actually generate object code, such as THEN and SELECT, cannot be traced. Some of the options discussed in Section "REXX Command Options" disable certain features of the REXX language to improve performance or save memory: * /NS disables tracing of the source. It also causes the SOURCELINE function to give an error if called with an argument, and to return 0 if called with no arguments. * /NM inhibits source tracing, line numbers in error messages, and maintenance of the SIGL variable. * /NT prevents tracing of results and labels. * /O forces /NS to be in effect when the program is subsequently run, and makes /NT the default. 2.6. Additional Language Features Two features have been added to the language in : * A new TRACE prefix, $, is provided. TRACE output goes to the PC's display by default. $ is a toggle that routes the output to the PC's printer or back to the PC's display. * Two operands for the OPTIONS instruction are provided to allow user control of Personal REXX interaction with COMMAND.COM on a program-by-program basis. The arguments are NEWCOM and NONEWCOM. In addition, all of the built-in functions described in Chapter 8, "Personal REXX Utility Functions" are supported by but are not part of the language as such. Built-in functions implemented in IBM's OS/2 REXX are also supported. This includes BEEP, DIRECTORY, ENDLOCAL, FILESPEC, and SETLOCAL. Personal REXX User's Guide, Version 3.0 Index page 1 ======================================================================== Index _____ CHARS function ... 2.4 OS/2 CMS compatibility with IBM OS/2 compatibility with CMS REXX ... 2.3 REXX ... 2.3 Comments, not required ... 2.3 PARSE instruction ... 2.1, 2.3-2.4 Compatibility ... 2.1 extensions to ... 2.5 Reference manuals implementation limits ... 2.2 SAA Procedures Language with CMS REXX ... 2.3 Reference ... 2.1 with IBM OS/2 REXX ... 2.3 System Product Interpreter with REXX 4.0 ... 2.4 Reference ... 2.1 The REXX Language ... 2.1 DATE function ... 2.4 REXX Disk I/O maximum program size ... 2.2 read/write pointers ... 2.3 REXX command options Documentation /NM ... 2.5 SAA Procedures Language /NS ... 2.5 Reference ... 2.1 /NT ... 2.5 System Product Interpreter /O ... 2.5 Reference ... 2.1 The REXX Language ... 2.1 SELECT instruction ... 2.5 SIGNAL instruction ... 2.5 Extensions Source program handling ... 2.4 TRACE $ prefix ... 2.5 SOURCELINE function ... 2.5 EXTERNALS function ... 2.4 THEN instruction ... 2.5 Files TRACE instruction maximum number open ... 2.2 $ prefix ... 2.5 Functions, built-in Tracing CHARS ... 2.4 to printer ... 2.5 DATE ... 2.4 EXTERNALS ... 2.4 USERID function ... 2.3 LINEIN ... 2.4 LINEOUT ... 2.4 Version numbers LINES ... 2.4 REXX language ... 2.1 SOURCELINE ... 2.5 USERID ... 2.3 Implementation limits ... 2.2 Limits ... 2.2 LINEIN function ... 2.4 LINEOUT function ... 2.4 LINES function ... 2.4 Negation symbol ... 2.3 OPTIONS instruction ... 2.5