REXX Programming For TSO: Advanced Concepts: Dan O'Dea September 2, 2004
REXX Programming For TSO: Advanced Concepts: Dan O'Dea September 2, 2004
REXX Programming For TSO: Advanced Concepts: Dan O'Dea September 2, 2004
For TSO:
Advanced Concepts
Dan O’Dea
September 2, 2004
1
Table of Contents
Introduction . . . . . . . . . . . . . . . . . 3
Goals of the course. . . . . . . . . . . . . 3
More on syntax 4
Comments . . . . . . . . . . . . . . . . . . 4
Handling Hexadecimals. . . . . . . . . . . . 4
More on If/Then 5
Using words to indicate success or failure . 5
Boolean operators. . . . . . . . . . . . . . 7
Passing Parameters 8
Using keyword parameters . . . . . . . . . . 8
Parsing 9
Using columns. . . . . . . . . . . . . . . . 9
Using literals . . . . . . . . . . . . . . . 10
PARSE VALUE. . . . . . . . . . . . . . . . . 10
PARSE SOURCE . . . . . . . . . . . . . . . . 10
OutTrap Restrictions 12
Addressing Environments 14
Stem Variables 15
System Information 16
SYSVAR . . . . . . . . . . . . . . . . . . . 16
MVSVAR . . . . . . . . . . . . . . . . . . . 18
2
Introduction
The first part of this course provided you with basic learning needed to code REXX
execs. Some topics were glossed over, or skipped entirely. REXX performance was not
discussed. Finally, some REXX features can be used to make your program more
readable, such as word tests (something like the COBOL 88 level variable).
This document corrects those “oversights.” While the first document often used
simplified examples, this document uses real-world examples wherever possible.
When you complete the course you will be able to do the following.
3
More on REXX Syntax
Comments are started by “/*” and ended with “*/”. They can go anywhere in the
program. For example, this is a wing comment:
This is a box comment. Note each line is not closed. Only the first and last lines have
slashes; the first slash starts the comment, and the last finishes it.
Longer comments are more efficient because the REXX interpreter opens comment
processing each time it encounters a /*. By coding only one set around multiple
comment lines, the interpreter does less work. This is especially important in loops.
Make a box comment above a loop to explain everything the loop does. If the
comments are in the loop, they are scanned each pass through the loop. To avoid
unnecessary loop processing, don’t comment within the loop.
Hexadecimal Values
You may recall that hexadecimal numbers may be used. Here is a piece of code
showing how interpreting hex characters is used to determine what LPAR you’re running
on. How it works:
First, convert the 4 bytes of system storage area at location x’10’ to hex. Convert that
to decimal for math, then add the offset to the storage location of the system name (in this
case, hex 154 / decimal 340). Convert the result back to hex. Then get the 8 bytes of
storage at the resulting address and strip the spaces from it.
/*------------*
* What LPAR? *
*------------*/
CVT = C2X(STORAGE(10,4))
CVT_154 = D2X(X2D(CVT) + X2D(154))
SID = STRIP(STORAGE(CVT_154,8))
4
Conditional Operators
If expression
Then one instruction
Else one instruction
Internally, any IF statement operates by testing the outcome of a binary choice (yes or
no). We’ve seen the results of IF comparisons can be displayed as “1” (true) or “0”
(false). We can use this feature to our advantage.
In REXX, almost any statement can contain a variable to be resolved. Consider the
following:
Dan = 0
Say “Enter name”
Pull Name
If Name = “Dan” Then Dan = 1
If Dan
Then Say “Hi, Mr. O’Dea!”
Else Say “Hi,” Name“.”
REXX resolves the expression “If Dan” as either “If 0” or “If 1” depending on the
value in the variable Dan. “Dan” is called a noun switch. It’s COBOL equivalent is the
88 level.
There are two things to keep in mind when using this method:
1. You must be careful to set the switch to only zero or one. If the IF statement
finds any other value, your exec terminates with a syntax error.
2. You cannot negate the switch. To test for a negative condition, it’s a good idea to
code the statement positively, and use the NOP command to skip the THEN
condition. For example,
KSDS = 0
If <index> Then KSDS = 1
If KSDS
Then NOP /* Do nothing for KSDS */
Else Say “ESDS” /* Do ESDS stuff */
5
Conditional Operators Noun Switch Example
Here is part of the code from the stack example above. First, set the switch to 0
(off/false). Then, check if the dataset has an index component. If the index component
exists, turn the switch on. You can then check the switch in the IF.
KSDS = 0
CKIX = INDSN“.INDEX”
If SysDSN(CKIX) = “OK” Then KSDS = 1
::
If KSDS
Then Do
Queue " DATA (NAME("NewDSN".DATA)) -"
Queue " INDEX (NAME("NewDSN".INDEX))"
End
Else Queue " DATA (NAME("NewDSN".DATA))"
Here’s another piece of code used to scan a LISTC ENT() ALL output for the index
CI size:
If EndOfData
Then NOP
Else Do
Do Until LCA.I = "ATTRIBUTES"; I = I + 1; End
I = I + 1
ICIS = Strip(Right(Word(LCA.I,4),6),"L","-")
End
1. The “Do Until” scans the output from the previous CISIZE field (the one in the
data component section) until the index attributes section to skip over stuff we
don’t want. The index “I” must point to the next line after “ATTRIBUTES” to
get the CISZ.
2. The last line gets the fourth word of the output line, takes the last 6 characters of
that word, then removes leading dashes from it to give the CI size of the index.
Exercise #1
Modify the exec you wrote in Exercise #28 of the first class to use a noun switch
instead of expression tests when deciding what to do with the dataset.
6
Passing Information to the Exec
The ARG statement parses values passed to the program based on spaces or commas.
ARG is thus a positional operation selector. Here’s one way to code the ARG to enable
you to process passed parameters as keywords.
This is designed to perform a LISTC ENT() on without having to code ALL, GDG, or
other argument in any order. All these calls give the same result:
Exercise #2
Modify the exec you wrote in Exercise #16 from the first course to accept the unit and
amount values as keywords. Only accept two arguments. If one argument is bad, display
the syntax and exit.
7
Separating Data: Parsing
In the introduction we talked about parsing data by word or literal. You can also parse
data in other ways. Some of them are very useful.
You can parse by column number. It can get complicated because column numbers in
PARSE are not intuitive, but for some things it performs very well.
The PARSE command is very fast. It is faster than SUBSTR or the LEFT and RIGHT
functions. In loops running many times, PARSE using columns can save you some CPU.
When specifying column delimiters, the number to the left of the variable name is the
start column, while the number on the right is the end column, minus one. As you can
see in the example, sometimes this can be misleading:
%SPLITIT ABCDEFGHIJKLMNOP
•VAR1 = ABCD;
•VAR2 = EFGH;
•VAR3 = IJKL
%SPLITIT ABCDEFGHIJKLMNOP
•VAR1 = ABCD;
•VAR2 = GH;
•VAR3 = KL
Please don’t give up on this just because it looks complicated. You’ll see why below.
8
PARSE using Literals
When parsing using a literal, you can use any literal. Consider this:
After the PARSE the variable DSN contains “YOUR.DSN”. However, you can skip the
LINE1 assignment statement by using PARSE VALUE and the literal itself.
PARSE VALUE
PARSE VALUE needs the keyword WITH to tell where the template begins. This
can be used with functions as well as variables.
PARSE SOURCE
PARSE SOURCE asks TSO for information about the way the exec was run.
where:
•OP_SYSTEM is TSO
•HOW_CALLED is either COMMAND, SUBROUTINE, or FUNCTION
•EXEC_NAME is the name of the exec in upper case
•DD_NAME the exec was found in SYSEXEC or SYSPROC
•DATASET_NAME is the DSN the exec was in when called implicitly
•AS_CALLED is the name the exec was invoked by. May be in lower case
when called implicitly
•DEFAULT_ADDRESS is the initial address environment. This is usually TSO,
MVS, ISPEXEC (i. e. ISPF), or ISREDIT (the ISPF Editor)
•NAME_OF_ADDRESS_SPACE is one of MVS, TSO, or ISPF.
9
Parsing continued
Here are several examples of using PARSE rather than a built-in functions or many
assignments.
Take the third word from the variable INVAR and call it WORD3. The PARSE
instructions are much faster, and if you don’t need the previous two words use the “.”.
1. WORD3 = WORD(INVAR,3)
2. WORD3 = SUBWORD(INVAR,1,3)
3. PARSE VAR INVAR JUNK1 JUNK2 WORD3 .
4. PARSE VAR INVAR . . WORD3 .
Take the left five bytes from the variable AMBI and call it LEFTY. LEFT is faster
then SUBSTR. Again, the PARSE instruction are much faster than either.
1. LEFTY = Left(AMBI,5)
2. LEFTY = SUBSTR(AMBI,1,5)
3. PARSE VAR AMBI 1 LEFTY 6 .
Take the right five bytes from the variable AMBI and call it RT. Note there is an
extra step for the SUBSTR example, and no PARSE example. Parse could be used if you
knew, when you coded, how long AMBI would be. The single function call is faster.
1. RT = Right(AMBI,5)
2. ST = Length(AMBI) – 4; RT = SUBSTR(AMBI,ST,5)
Take the middle five bytes from the variable AMBI, where the middle is known
before coding. The PARSE instruction is faster.
1. MID = SUBSTR(AMBI,5,5)
2. PARSE VAR AMBI . 5 MID 10 .
Set A, B, C, and D to null. The PARSE instruction is faster. You can also use this
method to set all variables to zero.
10
Exercise #3
Write an exec to parse the date into month, day, and year. Use Parse Value.
Redisplay the date in the following formats:
11
Trace Output Line Codes
When running a program in trace mode, the REXX interpreter prefixes each line of
trace output with a three character symbol. These symbols tell you what is happening at
each stage of REXX’s handling of the statement.
OutTrap Restrictions
Not all output from TSO commands can be trapped by REXX. Whether you can trap
output depends on how the command sends its output to the screen. Outtrap cannot
capture lines produced by the following.
•TPUT
•WTO macro
•Messages issued by REXX (IRX*)
•Trace output messages
An example of a command you cannot capture the output for is CONCAT. Try it.
12
Arithmetic Operations in REXX
As you know, there are certain characters you can use in any arithmetic statement, and
some you can’t. In the first half of the class we saw the basic arithmetic operators.
Parentheses can be used, as well as spaces to separate terms, periods for decimals, and the
letters A – F for hexadecimal numbers. Here is a clever way to make sure your
expression (EXPR) contains only valid characters:
If Verify(EXPR,'1234567890ABCDEF+-*/% ()') = 0
The VERIFY function checks to see if all the characters in the first string are
contained in the second string.
The FOR tells REXX to run this loop 22 times no matter what else happens.
13
Talking to the Environment
For each ADDRESS command, REXX opens a command processor link. To improve
performance, then, it is good practice to use as few ADDRESS commands as possible.
For example, the following code is a subroutine to format and submit a piece of JCL.
Arg JCLSKEL
Address ISPEXEC
DO0JOBNM = $JobName()
"FTOPEN TEMP"
"VGET (ZTEMPF) SHARED"
"FTINCL" JCLSKEL
"FTCLOSE"
ZEDSMSG = DO0JOBNM "SUBMITTED"
ZEDLMSG = "Batch job” DO0JOBNM “submitted.”
"SETMSG MSG(ISRZ000)"
Address TSO "SUBMIT DATASET('"ZTEMPF"')"
Return
This is the same code, with the ADDRESS commands as part of the code.
Arg JCLSKEL
DO0JOBNM = $JobName()
Address ISPEXEC "FTOPEN TEMP"
Address ISPEXEC "VGET (ZTEMPF) SHARED"
Address ISPEXEC "FTINCL" JCLSKEL
Address ISPEXEC "FTCLOSE"
ZEDSMSG = "JOB SUBMITTED"
ZEDLMSG = "Batch job” DO0JOBNM “was submitted.”
Address ISPEXEC "SETMSG MSG(ISRZ000)"
Address TSO "SUBMIT DATASET('"ZTEMPF"')"
Return
While both produce the same results, the first one uses less CPU and runs slightly
faster because there’s no address switching except for the SUBMIT command passed to
TSO. For short execs the difference is so small it’s almost meaningless, but for many
consecutive calls, or calls within a loop, the difference is measurable.
14
More on Stem Variables
So far we’ve only seen stem variables with a single index that’s always a number.
Stem variables are more flexible than that.
For example, although a stem variable has a stem and an index, a single entry of a
stem variable is just like any other, non-stem variable. We can set any single entry of a
stem variable to be a stem variable using a second index. Here is a piece of code to
initializes a chessboard, a two-dimensional array of eight ranks and eight files.
Do R = 1 To 8
Do F = 1 To 8
ChessBoard.R.F = "."
End
End
The stem index does not have to be a number. REXX understands the use of a letter
rather than a number. For example, try this.
/* REXX */
Do Forever
Say “What is your name (END to stop)?”
Pull NAME
If NAME = END Then Leave
Say “Thanks,” NAME “, how old are you?”
Pull HOW_OLD
If DataType(HOW_OLD) <> “NUM”
Then Say “Not valid age, rejected.”
Else AGE.NAME = HOW_OLD
End
Do Forever
Say “Whose age would you like”,
“(END to stop)?”
Pull NAME
If NAME = “END” Then Leave
If SYMBOL(‘AGE.NAME’) <> “VAR”
Then Say “Name not in memory.”
Else Say NAME “is” AGE.NAME “years old.”
End
15
System Variables
REXX provides two sets of system variables. The first set is SYSVAR.
16
System Variables: SYSVAR notes
1. SYSVAR can only be used in TSO/E environments (i. e. not VM, OS/2, etc.).
3. SYSPCMD and SYSSCMD are connected. For example, if you’re running the TEST
TSO command (debugging a program), SYSSCMD might return EQUATE (the last
TEST command run) while SYSPCMD would return TEST.
5. Some CLIST control variables do not apply to REXX. REXX has “replacements” for
these variables.
•SYSDATE ===> DATE(“U”)
•SYSJDATE ===> DATE(“J”)
•SYSSDATE ===> DATE(“O”)
•SYSSTIME ===> SUBSTR(TIME(),1,5)
•SYSTIME ===> TIME()
Examples:
17
System Variables: MVSVAR
When using SYMDEF, you must supply both the word SYMDEF and the symbolic
variable name. Variables must be defined in the SYS1.PARMLIB member IEASYMxx.
Exercise #4
Write an exec to print a calendar month, given the name of the month and the name of
the first day of the month. For example, CALPRT JANUARY MONDAY.
Exercise #5
Write an exec to fill a 3X3 array with “#” by having the user enter a row name (letter)
and column number. If the row and column are “equal”, fill the slot with a “$” instead.
Print the array at the end. The array should look like this:
1 2 3
A $ # #
B # $ #
C # # $
18
Other Performance Concerns
Here are some other performance tips. They are not in any particular order.
Remember, you get more bang for your buck in a loop than in a single instruction. Don’t
sacrifice readability for speed unless you absolutely need to.
o Shorter variable names are faster. Use abbreviated names if they’re clear enough.
For example, Social_Security_Number is valid, but SSN is just as clear and more
efficient.
o When known ahead of time, use literals or constants rather than variables.
o Code subroutines as close to the call as possible (without sacrificing readability). Use
SIGNAL to get around the code, if necessary.
o When grouping subroutines, place the most often used routine first.
o Consider a subroutine when a large number of statements are needed for something
that is often NOT executed.
o When calling subroutines or functions don’t specify the defaults, and pass literals
rather than variables when possible.
o Avoid using functions within functions. For example, a) is more efficient than b)
despite using more lines of code:
a)PT = right(strip(cmddata),3)
b)PL = strip(cmddata); PT = right(PL,3)
o Avoid DATE and TIME functions in loops and subroutines if possible.
19
Putting It All Together
You can combine functions to manage records. Here are some examples.
1.MCVLOC = Pos("MODCVOL",SYMB)
2.LHLen = MCVLOC - 1
3.RHLen = 71 - MCVLOC
4.LHalf = Substr(Source.SOURCEI,1,LHLen)
5.RHalf = Substr(Source.SOURCEI,MCVLOC,RHLen))
6.Parse Var RHalf MODCVOL "," RRHalf
7. If Right(Strip(Rhalf),1) = ","
8. Then SYMB = LHalf","RRHalf
9. Else SYMB = LHalf||RRHalf
10.FLen = Length(SYMB)
11.If Right(SYMB,2) = ",,"
12. Then SYMB = Substr(SYMB,1,FLen-1)
20
Combinations of Functions continued
Suppose you want neater JCL. Your idea is to force the EXEC or DD to always make
room for an 8-byte step name or DD name, followed by one space, followed by either
“DD” or “EXEC.” All other arguments follow the “DD” or “EXEC” after one space.
21
Combinations of Functions continued
At times, people doing data security need to scan their datasets to see what is and what
is not protected, and what profiles are in WARNING mode (i.e. the profile exists but is
not observed). Here is some code to do this check against any dataset level. Please note
the code is split onto two pages, with the explanation on a third page.
22
RIB exec Page 2
This section of the exec determines the profile type and if it is in warning. Finally, the
exec creates an output report.
40. Select
41. When WARN = "NO" Then Do
42. W = "HARDCHK"; GOODPROF = GOODPROF + 1
43. End
44. When WARN = "YES" Then Do
45. W = "WARNING"; WARNPROF = WARNPROF + 1
46. End
47. Otherwise W = "UNKNOWN"
48. End
49. OutIX = OutIX + 1
50. OutLine.OutIX = Copies(" ",79)
51. OutLine.OutIX = Overlay(RACFDS,OutLine.OutIX,1)
52. OutLine.OutIX = Overlay(PROFILE,OutLine.OutIX,36)
53. OutLine.OutIX = Overlay(UACC,OutLine.OutIX,64)
54. OutLine.OutIX = Overlay(W,OutLine.OutIX,72)
55.End L /* Do group from Line 19 */
56.UNKNOWN = TotalDSN - GOODPROF - WARNPROF - NOPROF
57.PROFILES = GOODPROF + WARNPROF
58. OutLine.5 = "RACF report for dataset level" LEVEL":"
59. OutLine.6 = ""
60. OutLine.7 = "TOTAL DATASETS: ",
61. Right(" "TotalDSN,6)
62. OutLine.8 = "DATASETS WITH PROFILES: ",
63. Right(" "PROFILES,6)
64. OutLine.9 = "DATASETS IN WARNING: ",
65. Right(" "WARNPROF,6)
66. OutLine.10 = "PROTECTED DATASETS: ",
67. Right(" "GOODPROF,6)
68. OutLine.11 = "UNPROTECTED DATASETS: ",
69. Right(" "NOPROF,6)
70. OutLine.12 = "INSUFFICIENT AUTHORITY: ",
71. Right(" "UNKNOWN,6)
72. OutLine.0 = OutIX
73."ATTRIB FB80 RECFM(F B) LRECL(80)"
74.OutDSN = LEVEL".DATA"
75. "DELETE" OUTDSN
76. "ALLOC DA("OUTDSN") F(OUTFILE) NEW SPACE(5 5) TRACKS”,
77. “REUSE USING(FB80) RELEASE"
78."EXECIO " OutLine.0 " DISKW OUTFILE (STEM OutLine. FINIS"
79. Say
80.Say "Output written to" OutDSN"."
81.“FREE F(OUTFILE) ATTR(FB80)”
82.Exit
23
RIB exec Page 3
Lines 7 – 16 set the headers. Note several lines are left blank at this time. This makes
room for the summary lines, whose values cannot be determined until the exec
ends. The OVERLAY function lets me align the headers to specific columns.
Finally, Line 17 points the output stem index to the first blank line after the dataset
report header line.
The select group at Line 19 keeps non-VSAM and VSAM dataset names, and skips all
other lines from the LISTC.
Line 26 does the RACF command LISTDSD to get the profile information.
Lines 37 – 39 writes a note if you don’t have the authority to list the profile.
The select group at Line 40 notes whether the profile is in WARNING or not.
Lines 56 – 72 format the summary section. This section uses the blank lines we left back
on lines 7 – 16.
The remaining lines allocate the output dataset and write the report stem to the output
dataset.
24
This Page Intentionally Left Blank
25
Appendix A: A Few Simple Edit Macros
These are a few, simple edit macros to show how many macros are just a string of edit
commands you might use frequently. Each is explained in some detail.
When starting any edit session, ISPF can run a macro on the dataset before turning
control over to you. This macro is called an initial edit macro. You can specify an initial
macro on the edit screen. If you want the macro to run every time, though, it’s just as
easy to tell ISPF what it is. This is done in two easy steps.
STEP 1: Code the macro. Here is an example. The code is COURIER; comments are
made in Arial and underscored.
Address ISREDIT
"MACRO"
"PROFILE UNLOCK" - unlock the edit profile of the current dataset.
"BOUNDS" - Reset BOUNDS to the full LRECL.
"NULLS ON" - Lets me insert (ignores spaces on end of lines).
"HILITE ON" - Turns on highlighting
CURMBR = ""
"(CURMBR) = MEMBER" Checks if editing a PDS. If so, turn on STATS.
If CURMBR <> "" Then "STATS ON"
"RESET" - Clear all messages.
Exit
STEP 2: set the initial macro. You must use the VPUT Dialog Manager service to do
this. So you don’t have to remember (or know) how to do that, run this command:
Note: some ISPF dialogs run in their own application ID. Should you enter a dialog
and your edit macro doesn’t work, try running SETZIMAC again.
26
More Simple Edit Macros
These two edit macros change all upper-case characters to lower-case, or vice-versa.
Note they’re just a single edit command. This could just as easily be set to a function
key.
Address ISREDIT
"MACRO"
"C P'>' P'<' ALL"
Exit
Address ISREDIT
"MACRO"
"C P'<' P'>' ALL"
Exit
This macro excludes all lines, then does a find on a passed string. The purpose is to
see only what you want to see. Note the passed parameter may include quotes, column
numbers, and other FIND operands (except ALL, of course). For sample calls, see below
the macro.
Address ISREDIT
"MACRO (FPARM)"
"EXCLUDE ALL"
"FIND ALL "FPARM
Exit 0
Sample calls:
27
Appendix B: Solutions to Exercises
Exercise 1, page 6
Modify the exec you wrote in Exercise #28 to use a noun switch instead of expression
tests when deciding what to do with the dataset.
28
Exercise 2, Page 7
Modify the exec you wrote in Exercise #16 from the first course to accept the unit and
amount values as keywords. Only accept two arguments. If one argument is bad, display
the syntax and exit.
Select
When UNIT = "LITER"
Then Say AMT UNIT "is" AMT*1.057 "quart."
When UNIT = "QUART"
Then Say AMT UNIT "is" AMT*.946 "liter."
When UNIT = "MILE"
Then Say AMT UNIT "is" AMT*1.6 "kilometer."
When UNIT = "KILOMETER"
Then Say AMT UNIT "is" AMT*.625 "mile."
End
29
Exercise 3, Page 11
Write an exec to parse the date into month, day, and year. Display them in the
following formats:
30
Exercise 4, Page 19
Write an exec to print a calendar month, given the name of the month and the name of
the first day of the month. Assume the first day of the week is Sunday. For example,
CALPRT JANUARY MONDAY should start the calendar on Monday, January 1. Use
the CENTER function to display the month name.
/*-------------------------------*
* Init month table with blanks. *
*-------------------------------*/
Do I = 1 to 6
Do J = 1 to 7
Week.I.J = " "
End J
End I
/*------------------------------------------*
* Init fields: *
* WeekDay is the day of the week; *
* DayNumber is the day of the month; *
*------------------------------------------*/
WeekDay = 1; DayNumber = 1
/*---------------------------------*
* Set the first day of the month. *
*---------------------------------*/
Select
When Day = "SUNDAY" Then WeekDay = 1
When Day = "MONDAY" Then WeekDay = 2
When Day = "TUESDAY" Then WeekDay = 3
When Day = "WEDNESDAY" Then WeekDay = 4
When Day = "THURSDAY" Then WeekDay = 5
When Day = "FRIDAY" Then WeekDay = 6
When Day = "SATURDAY" Then WeekDay = 7
Otherwise Do
Say "Invalid day" Day"."
Exit
End
End
31
Exercise 4, Page 19, answer continued
/*---------------------------------------*
* Load the table. Start at the current *
* weekday, then reset it after Week 1. *
* Skip out when at end of month. *
*---------------------------------------*/
Do WeekNumber = 1 to 6
Do WeekDay = WeekDay to 7
If Length(DayNumber) = 1
Then DayWord = " "DayNumber
Else DayWord = DayNumber
Week.WeekNumber.WeekDay = DayWord
DayNumber = DayNumber + 1
If DayNumber > DayLimit Then Signal EndOfMonth
End
WeekDay = 1
End
EndOfMonth:
"CLRSCRN"
Say
Say Center(Month,21)
Say " S M T W T F S"
Do WeekNumber = 1 to 6
Say " "Week.WeekNumber.1 Week.WeekNumber.2,
Week.WeekNumber.3 Week.WeekNumber.4 Week.WeekNumber.5,
Week.WeekNumber.6 Week.WeekNumber.7
End
32
Exercise 5, Page 19
Write an exec to fill a 3X3 array with “#” by having the user enter a row name (letter)
and column number. If the row and column are “equal”, fill the slot with a “$” instead.
Print the array at the end. The array should look like this:
1 2 3
A $ # #
B # $ #
C # # $
HINT: you will need the TRANSLATE function to do this exec cleanly. Guidance is
provided if desired.
/*------------------------------------*
* SLOTS is the number of full slots; *
* ArrayFull says the array is full. *
*------------------------------------*/
Slots = 0; ArrayFull = 0
33
Exercise 5, Page 19 continued
/*---------------------------*
* Loop until array is full. *
*---------------------------*/
Do Until ArrayFull
Say "Enter a row (A, B, or C) and a column (1, 2, or 3)."
Pull Row Col
Say
If Pos(Row,"ABC") = 0 Then Do
Say "Row" Row "invalid, try again."
Iterate
End
If Pos(Col,"123") = 0 Then Do
Say "Column" Col "invalid, try again."
Iterate
End
RowNum = Translate(Row,"123","ABC")
If RowNum = Col
Then Array.Row.Col = "$"
Else Array.Row.Col = "#"
Slots = Slots + 1
If Slots = 9 Then ArrayFull = 1
End
/*--------------------*
* Display the array. *
*--------------------*/
Row = "A"
Say "A" Array.Row.1 Array.Row.2 Array.Row.3
Row = "B"
Say "B" Array.Row.1 Array.Row.2 Array.Row.3
Row = "C"
Say "C" Array.Row.1 Array.Row.2 Array.Row.3
34