Next Meeting: Sat, TBD
Meeting Directions

 Be a Member Join SCOUG

Help with Searching

20 Most Recent Documents
Search Archives
Index by date, title, author, category.

Features:

Mr. Know-It-All
Ink

SCOUG:

Email Lists

Online Chats

Pictures from Sept. 1999

The views expressed in articles on this site are those of their authors.

SCOUG, Warp Expo West, and Warpfest are trademarks of the Southern California OS/2 User Group. OS/2, Workplace Shell, and IBM are registered trademarks of International Business Machines Corporation. All other trademarks remain the property of their respective owners.

The Southern California OS/2 User Group
USA
Rexx Programming Potpourri
Melanie Chernoy, SCOUG Member

### Introduction

This session will be a mixture of some different and interesting REXX programming techniques. Melanie will discuss Stem Variables and the Parse Command, two unique and powerful features of REXX. She will also show how to use VREXX2, which provides a way to add simple window, dialog and menu facilities to your REXX programs.

### Compound (Stem) Variables

Think of compound variables as you would an array in other programming languages. But these arrays have some special features. Not only do you not have to worry about how big your arrays are, but the array index/subscript doesn't have to be a number.

Here is a simple example of using a REXX compound variable as an array. It will set values for "Array.1" as "1", "Array.2" as "2", ... and "Array.5" as "5".

Do i=1 to 5 Array.i =i End

You can also create 2 dimensional arrays in a similar manner. This example will create a multiplication table, where "Array.1.1" is "1" and "Array.5.5" is "25".

Do i=1 to 5 Do j=1 to 5 Array.i.j = i*j End End There is a common convention to use a tail of "0" to store the count of values in an array: Array.0 = 5 Do i=1 to Array.0 Array.i =i End There are some commands/functions in Rexx (like the EXECIO command in the VM/CMS version and the SysFileTree RexxUtil function in the OS/2 version) that will automatically put the count of returned values into the ".0" tail when storing values into a stem variable. Other times, you must keep track of the number of array elements yourself.

Even used as a simple array, stem variables have a very neat feature. You can initialize the entire array with one command. Just specify the stem name without a tail in your assignment command. For example, to set the default value of "Array" to "0", use "Array. = 0".

Stem variables can also be associative arrays by using non-numbers as the tail. Here is a simple program to show a phone number for a specified name.

/* Phone.Cmd */ Phone. = 'Unlisted' Phone.Fred = '213-555-1212' Phone.Wilma = '714-555-1212' Phone.Pebbles = '310-555-1212' Say 'Enter a name:' Pull Name . Say Name||&quot;'s phone number is:&quot; Phone.Name Return

Here is another example that will determine if a letter is a vowel.

/* IsVowel.Cmd */ IsVowel. = 0 IsVowel.A = 1 IsVowel.E = 1 IsVowel.I = 1 IsVowel.O = 1 IsVowel.U = 1 IsVowel.Y = 1 Say 'Enter a Letter' Pull Letter If IsVowel.Letter Then Say 'Yes,' Letter 'is a vowel.' Else Say 'No,' Letter 'is not a vowel.' Return

### Parse Command

Generic form: Parse [upper] {what/where-from} {how}

The "what/where-from" can be:

Arg
Passed as an argument
LineIn()
From an input file
Pull
From the keyboard/buffer
Value ... with
From a specified expression
Var
From a variable
and the "how" can be:
... almost anything!
Standard blank-delimited "tokens" (words)
Matching specified characters
Separated by specific column or position numbers (like a substring)
By relative column positioning (+1, -3, etc.)
... or any combination of the above methods

#### By Tokens

Example:Parse value "ab cde fghijk lmnop qrstuvw xyz" with v1 v2 v3 v4 v5 Will result in:
v1='ab'
v2='cde'
v3='fghijk'
v4='lmnop'
v5='qrstuvw xyz'

The special things to note here, are that the tokenizing uses the blanks that separate each word or token and the last variable is handled differently than the others. The first blank after the preceding token is used and EVERYTHING else inthe string, including any leading, imbedded, and trailing blanks will go with the last variable. For example:

This:

Parse value 'ab cd ef ' with v1 v2 v3 Will result in:
v1='ab'
v2='cd'
v3=' ef '

Notice that both the extra leading and trailing blanks will be added to the last variable. It is easy to eliminate this inconsistency. Just add the magic "." to the end of your variable list. This will now become the "leftover variable" and eliminate any unexpected results from this parsing "feature".

#### By Matching Characters

This: Parse value "ab; cde; fghijk lmnop; qrs;tuv; wxyz" with v1 ';' v2 ';' v3 ';' v4 ';' v5 Will result in:
v1='ab'
v2=' cde'
v3=' fghijk lmnop'
v4=' qrs'
v5='tuv; wxyz

Notice that the matching character is "used up" by the parse command and does not appear in any of the variables. (The ';' is in v5 because it is the 5th one in the input string and wasn't used as a pattern matching character.)

#### By Columns/Positions

This: Parse value 'ab cd ef gh ij' with 1 v1 3 v2 5 v3 7 v4 9 v5 Will result in:
v1='ab'
v2=' c'
v3='d '
v4='ef'
v5=' gh ij'

#### By Relative Columns/Positions

This: Parse value 'ab cd ef gh ij' with 1 v1 +2 v2 +2 v3 +2 v4 +2 v5 Will result in:
v1='ab'
v2=' c'
v3='d '
v4='ef'
v5=' gh ij'

One of the more powerful features of the parse command is the ability to parse the same input string multiple ways in the same command. This:

Parse value 'ab cd ef gh ij' with 1 v1 3 v2 5 v3 1 v4 9 v5 Will result in:
v1='ab'
v2=' c'
v3='d ef gh ij'
v4='ab cd ef'
v5=' gh ij'

Wow! How did that happen? By specifying column 1 again, in the middle of the variable list, REXX will "back up" and re-parse the input string starting over again at column 1. Notice that doing this makes v3 act kind of like the "last" variable, and it gets the value of the rest of the input string. So, if there were any trailing blanks at the end of the input string, there would be trailing blanks at the end of variable v3.

Using negative relative columns will produce a similar result. This:

Parse value 'ab cd ef gh ij' with 1 v1 +2 v2 -2 v3 +2 v4 -2 v5 Will result in:
v1='ab'
v2=' cd ef gh ij'
v3= 'ab'
v4=' cd ef gh ij'
v5='ab cd ef gh ij'

But what if you didn't want v3 to get the rest of the input string? Just remember to use that magic '.': This:

Parse value 'ab cd ef gh ij' with 1 v1 3 v2 5 v3 7 . 1 v4 9 v5 Will result in:
v1='ab'
v2=' c'
v3='d '
v4='ab cd ef'
v5=' gh ij'

### VREXX

VREXX is Visual REXX for OS/2 Presentation Manager! VREXX allows you to write standard OS/2 REXX command files which can create and control PM windows. VREXX also dynamically constructs dialog boxes which allow filename selection, font and color selection, string input, and message display. Other functions allow selection of items through listbox, radiobutton and checkbox dialogs.

VREXX also supports graphical text output in 14 different fonts, as well as pixel, marker, polyline, polygon, spline and arc drawing functions.

You DO NOT need to be a PM programmer to use VREXX! Here is a complete VREXX hello world program, with an additional message box at the end:

/* HELLO.CMD */ /* initialize VREXX external functions */ '@echo off' call RXFUNCADD 'VInit', 'VREXX', 'VINIT' initcode = VInit() if initcode = 'ERROR' then signal CLEANUP signal on failure name CLEANUP signal on halt name CLEANUP signal on syntax name CLEANUP /* do hello, world window */ position.left = 25 /* put window in the center of the screen */ position.bottom = 25 /* coordinates are % of screen */ position.right = 75 position.top = 75 /* open the window and draw the text string */ id = VOpenWindow('My VREXX Window', 'WHITE', position) call VSay id, 300, 500, 'Hello, world' /* put up a 1 line message box - when user presses ok, end the program */ msg.0 = 1 msg.1 = 'Press OK to end the HELLO.CMD program' call VMsgBox 'HELLO.CMD', msg, 1 call VCloseWindow id CLEANUP: call VExit /* terminate VREXX */ exit Run the procedure just like any other REXX procedure, by typing hello or start hello.cmd at an OS/2 command prompt. On-line help is available by typing: view vrexx.inf

VREXX was written by Richard B. Lam at the IBM T.J. Watson Research Center.

#### VREXX Samples

 VMsgBox with button style 2
Buttons have different styles. 1=OK, 2=CANCEL, 3=OK and CANCEL, 4=YES, 5=NO, 6=YES and No
 VInputBox with button style 3
 VMsgBox with button style 2
 VMultBox with button style 3
 VFontBox with button style 3
 VCheckBox with button style 1

#### Sample Program: AREACODE.CMD

The original REXX version: /* REXX Exec to display the state and time zone for a given areacode */ Arg areacode . If areacode = '?' Then Signal Explain If areacode = '' Then Do Say 'Enter Areacode:' Pull areacode . End /* Set up area code stem variables */ Call INIT If state.areacode = '' Then Say 'Areacode' areacode 'not found.' Else Do Say 'Areacode ==>' areacode Say 'State ==>' state.areacode Say 'Time Zone ==>' zone.areacode End Return /* ------------------------------------------------------------------ */ Explain: Say 'The AREACODE exec is used to get the location (state)' Say 'and time zone of a given area code.' Say '' Say 'The time zones indicated are:' Say '=======================================' Say 'Hawaii (Pacific time -3 hours)' Say 'Alaska (Pacific time -1 hour)' Say 'Pacific' Say 'Mountain (Pacific time +1 hour)' Say 'Central (Pacific time +2 hours)' Say 'Eastern (Pacific time +3 hours)' Say 'Atlantic (Pacific time +4 hours)' Return /* ------------------------------------------------------------------ */ Init: State.=''; Zone. = '' State.201='New Jersey'; Zone.201='Eastern' State.202='Washington DC'; Zone.202='Eastern' State.203='Connecticut'; Zone.203='Eastern' State.204='Manitoba, Canada'; Zone.204='Central' State.205='Alabama'; Zone.205='Central' /* Remainder of areacode data removed to conserve paper */ Return The new VREXX version: /* REXX Exec to display the state and time zone for a given areacode */ call RxFuncAdd 'VInit', 'VREXX', 'VINIT' initcode = VInit() if initcode = 'ERROR' then signal CLEANUP signal on error name CLEANUP signal on failure name CLEANUP signal on halt name CLEANUP signal on syntax name CLEANUP /* End of VREXX setup */ Call Explain /* Set up area code stem variables */ Call INIT prompt.0 = 1 prompt.1 = 'Enter Areacode:' button = VInputBox('AreaCode', prompt, 5, 3) Areacode = prompt.vstring If button <> 'OK' Then Signal CLEANUP If state.areacode = '' Then Do msg.0 = 1 msg.1 = 'Areacode' areacode 'not found.' Call VMsgBox 'Areacode: Error', msg, 1 End Else Do msg.0 = 3 msg.1 = 'Areacode ==>' areacode msg.2 = 'State ==>' state.areacode msg.3 = 'Time Zone ==>' zone.areacode Call VMsgBox 'Areacode: Results', msg, 1 End /* Close up and Exit */ CLEANUP: Call VExit Return /* ------------------------------------------------------------------ */ Explain: msg.1 = 'AREACODE is used to get the location (state)' msg.2 = 'and time zone of a given area code.' msg.3 = '=======================================' msg.4 = 'Hawaii (Pacific time -3 hours)' msg.5 = 'Alaska (Pacific time -1 hour)' msg.6 = 'Pacific' msg.7 = 'Mountain (Pacific time +1 hour)' msg.8 = 'Central (Pacific time +2 hours)' msg.9 = 'Eastern (Pacific time +3 hours)' msg.10 = 'Atlantic (Pacific time +4 hours)' msg.0 = 10 Ans = VMsgBox('Areacode: Help', msg, 3) If Ans = 'OK' Then Return Call VExit Exit /* ------------------------------------------------------------------ */ Init: State.=''; Zone. = '' State.201='New Jersey'; Zone.201='Eastern' State.202='Washington DC'; Zone.202='Eastern' State.203='Connecticut'; Zone.203='Eastern' State.204='Manitoba, Canada'; Zone.204='Central' State.205='Alabama'; Zone.205='Central' /* Remainder of areacode data removed to conserve paper */ Return