REXX: THE LANGUAGE
Источник: www.kyla.co.uk
Автор: Kyla Computing
Дата: 2007
Скачать в формате DOC
Скачать в формате HTML
Computer Training Courses
An Introduction to REXX Programming
Many computer languages have come and gone in my life, but REXX has stayed. Firstly, because I like it, in his book, Michael Cowlishaw says he developed it for programmers and not computers and that sums it up really. The other main reason why it has stuck, is because the two systems I use grow-up with are MVS - (IBM main frame platform) and my Amiga (the lost PC)! What have they in common? I am sure you can guess. On MVS I am playing with CA-7 or fixing its problems with in REXX, at home, I am generating and printing CA-7 training material. After many years it occurred to me in 1996 that I might as well do training in REXX, as well as CA-7 and this is something I do very regularly now.
Kyla Computing has a 3 REXX Perl training course. It has been designed for anybody Operations staff who get involved in basic Operations analyst functions, either reads others REXX programs or needing to manipulate data with a simple programming language.
Objectives
* After the course a student should be able to understand standard REXX syntax and coding rules.
* S/he will understand how to build REXX programs to read, process and write MVS files
* The course includes principles of good programming and good coding standards.
Prerequisites
* An understanding of standard MVS will be of benefit to a student.
* A student does not need any previous experience of any programming language.
Contents
The course contains details on the following area:
- Basic REXX syntax
- Standard Numeric Operations and function
- Variable types
- Decision processing syntax and facilities
- String functions
- Loop features
- User subroutines/functions
1. The language
1.1 Introduction to tutorial and REXX.
Comments
SAY instruction
Literal Strings.
Concatenation and Abuttal.
1.2 Basic Instructions
Assignment Instructions.
Simple Variables
Arthimetic Operartors
Expresssions
The PULL instruction
1.3 Conditional Processing
Comparative Operators
IF THEN & ELSE Instructions
Using DO & END with the IF Instructions
More Complex Comparisons
Strict Comparisons
Logical Comparisons
The 'NOT' Operator
The SELECT, WHEN, OTHERWISE and NOP instructions
1.4 Instruction Loops
DO Loops (with FOR, BY and TO)
DO WHILE and UNTIL loops
The LEAVE instruction
The ITERATE & FOREVER instructions
More DO loops
1.5 Other bits: INTERPRET and Functions
The INTERPRET instruction
Functions (Sub-routines)
The ARG instruction & RESULT variable
Built in Functions
2. String Functions
2.1 String Alignment Functions: CENTER, CENTRE, LEFT & RIGHT
2.2 String Comparison Functions: COMPARE & ABBREV
2.3 String Combining Functions: OVERLAY & INSERT
2.4 String Extraction Functions: SUBSTR & DELSTR
2.5 String Search Functions: INDEX, POS & LASTPOS
2.6 Other String Functions
2.6.1 The COPIES & RANDOM Functions
2.6.2 The LENGTH Function
2.6.3 The REVERSE string function
2.6.4 The STRIP function
2.6.5 The TRANSLATE function
2.6.6 The VERIFY function
2.6.7 The XRANGE function
3.3. Other Functions
3.1.1 The WORD function
3.1.2 The WORDINDEX function
3.1.3 The WORDPOS function
3.1.4 The WORDLENGTH function
3.1.5 The SUBWORD function
3.1.6 The DELWORD function
3.1.7 The WORDS function
3.1.8 The SPACE function
3.2 Number Functions(and instructions)
3.2.1 The TIME function
3.2.2 The DATE function
3.2.3 NUMERIC instruction, DIGITS & FUZZ functions
3.2.4 Exponential Notation functions
3.2.5 The FORMAT Function
3.2.6 The TRUNC Function
3.2.7 The SIGN Function
3.2.8 The ABS Function
3.2.9 The MIN & MAX Functions
3.2.10 The DATATYPE Function
3.3 The CONVERSION Functions
3.4 BIT Functions
3.5 Programming Functions and Instructions
3.5.1 The SIGNAL instruction / CONDITION function
3.5.2 The SIGL variable & SOURCELINE function
3.5.3 The RC variable & ERRORTEXT function
3.5.4 The VALUE function
3.5.5 The SYMBOL function
The PARSE Editor
1. The language
1.1 Introduction to tutorial and REXX.
Welcome to my REXX tutor! It's here for you to use. If you just want to play yourself please go ahead and enjoy it. Anyway enjoy it all, this is yours to keep for free! (That old fashioned word.) By the way if you haven't a clue what to look for because you're here to learn and you don't know what you need to know, then I would start here at the beginning and just grind your way down this whole file! - Good luck. Jim Barry of Kyla Computing ltd. 01273 841160
Comments
Comments always start /* and end */.
Top line Comment
If you thought that comment cards were optional you were wrong. On most platforms you must code a comment as the first line of your code. On some you even need to include the word REXX in this line.
Section Descriptions
Comments are important if you expect to alter your program in the future. There is a popular belief that you will remember your own code. Well just try writing a program without comments and returning to it two weeks later! Even very well written code is much easier to read when sectioned by comment lines describing what each block of code is doing.
Commenting out code.
Another purpose for comments is when you have code, which you do not want to execute. Note that it is alright to have a comment within a comment in most platforms (sometimes a compile option).
SAY instruction
template: SAY expression
examples: SAY "print this line"
SAY "Contents of A=" A
Why the word SAY and not the word PRINT I just don't know? All its doing is printing! If you don't know what I mean by EXPRESSIONS then look up that definition. Note that SAY on its own will print a blank line.
There really are very few rules when it comes to coding REXX. However it is important to understand the few that there are.
- The system presumes that each line is a new instruction unless you state you are continuing on another line by the use of a comma (we will see this in a minute).
- If you want two instructions on one line you will need to put a semi-colon between them.
- You can indent your instructions as much as you like; this is very useful when you are coding loops.
- You can also have as many blank lines as you like. These can be used to section your code and make it far more readable.
Literal Strings.
/* REXX program. Detailing Literals */
SAY "A Literal strings is a constant."
SAY 'Its delimiter can be single or '
SAY 'double quotes, which is "Useful".'
SAY "To continue on a second line",
"just end with a comma."
SAY 'We''ll see here two quotes together'
---------------------------------------------
A Literal strings is a constant.
Its delimiter can be single or
double quotes, which is "Useful".
To continue on a second line just end with a comma.
We'll see here two quotes together
These will litter your program. These really are important. You use them anywhere you want a CONSTANT. These are numbers or strings of letters, which will not change during the execution of the program.
Delimiters
Being able to use both double and single quotes as the delimiters is extremely clever and powerful. In the example you can see that both double and single quotes have been included in some of the literals. In each case the other type of quote has been use d as the delimiter. The other way you can code double or single quote within the string is by coding two next to each other, these will be printed as just one and it doesn't mean that its the end of the string.
Continuation
As with any command if you wish to continue on the next line a comma is put at the end of the line. Note that there is no SAY instruction on the second line and the comma is outside the delimiters.
Maximum length
The maximum length of a literal is platform specific but it will be at least 100 bytes and normally considerably more.
Concatenation and Abuttal.
/* REXX program. Concatenation & Abuttal */
SAY "When two literals are" "put next to each"
SAY "other they're printed" "with a single"
SAY "space between then"
SAY "To print without" || "gaps use these."
SAY "This is called" || " ABUTTAL"
----------------------------------------
When two literals are put next to each
other there printed with a single
space between then
To print withoutgaps use these.
This is called ABUTTAL
Concatenation
A very important concept it the automatic concatenation that applies within REXX. This is not only used for printing but in lots of other situations. The basic idea is that if you put two things (TOKENS) next to each other (in this example two literal strings), then they will be connected with a space. Note that they are always joined by one space regardless of how many spaces there are between the literal strings.
Abuttal
If you do not wish to have a space between the two tokens you must code two vertical lines (OR symbol). This system can apply to numbers as well, we haven't looked at these yet but what do you think A would equal here?
A = 5 || 7
1.2 Basic Instructions
Assignment Instructions.
/* REXX program. ASSIGNMENT Instructions. */
SAY "An assignment instruction is where tokens are"
SAY "are used to change the contents of a variable."
A = 2 + 3
SAY A "is the result of '2 + 3'"
B = A * 2
SAY B "is" A "times '2'"
----------------------------
An assignment instruction is where tokens are
are used to change the contents of a variable.
5 is the result of '2 + 3'
10 is 5 times '2'
Assignment instructions are used to change the contents of variables. Variables can be thought of as pigeonholes in the system where letters or a number can be held. We will deal with these in more details later. For now look at the example, Here the instruction A = 2 + 3 is adding two CONSTANTS and putting the result in the VARIABLE called A. Assignment instructions always have an equal sign in them and the name of a variable to the left of this. On the right of the equals sign are EXPRESSIONS and OPERATORS. In these examples the expressions are either single variables or constants but they can be far more complex. What do you think the contents of B would in after the following instructions?
A = 45
B = A || 1 + 3
Simple Variables
/* REXX program. Introducing SIMPLE VARIABLES. */
SAY "Simple variable names can contain these chars."
SAY "A - Z, a - z, 0 - 9, !, ?, #, $, @ & _ "
SAY "0 - 9 can not be used for the first character."
SAY "All lowercase chars. are converted to uppercase."
SAY "Variables are initialized with their name!"
TOTAL_a = 12 ; Text = "Page No." ; Page# = 1 + TOTAL_A
SAY ; SAY TOTAL_a " " Text || Page# " " NEW_var
----------------------------
Simple variable names can contain these chars.
A - Z, a - z, 0 - 9, !, ?, #, $, @ & _
0 - 9 can not be used for the first character.
All lowercase chars. are converted to uppercase.
Variables are initialized with their name!
12 Page No.13 NEW_VAR
Naming Convention
There are rules on the name of your variables. These are detailed in the example. There are several points worth noting. If your name contains lower case characters then these are automatically converted into capitals. See here how the variable TOTAL_a is used in one line and on the next it is referred to a TOTAL_A. Doing this for real would be considered a very sloppy habit but its quite legal (like a lot of sloppy habits).
Initialized Value
This is something worth remembering, in REXX you can use a variable even if you have not given it a value. In the example here we have only used NEW_var on a SAY statement. So what does it print? The answer is its own name (in capitals of course).
Data Type
Another rather weird thing about REXX is that you do not have to tell it whether your variable is going to contain a string of characters or a number - it just doesn't care!
Arthimetic Operartors
/* REXX program ARITHMETIC operators. */
SAY "ADD: 5 + 2 =" 5 + 2 ; SAY "SUBTRACT:2 - 5 =" 2 - 5
SAY "MULTIPLY:5 * -2 =" 5 * -2 ; SAY "DIVIDE: 5 / +2 =" 5 / +2
SAY "INT. DIV.5 % 2 =" 5 % 2 ; SAY "REMAINER:5 // 2 =" 5 // 2
SAY "POWER: 5 ** 2 =" 5 ** 2
SAY "ABUTTAL:5 || 2'00' =" 5 || 2'00'
SAY "USE BRACKETS FOR PRIORITY:" 7 || ((2 + 5) * 5)/3
----------------------------
ADD: 5 + 2 = 7
SUBTRACT:2 - 5 = -3
MULTIPLY:5 * -2 = -10
DIVIDE: 5 / +2 = 2.5
INT. DIV.5 % 2 = 2
REMAINER:5 // 2 = 1
POWER: 5 ** 2 = 25
ABUTTAL:5|| 2'00' = 5200
USE BRACKETS FOR PRIORITY: 711.6666667
Operators
Here you can see the different mathematical operators available. It all looks pretty straight forward until you come down to ABUTTAL - what is this doing here you may say? Well I said you could use concatenations for more than just pretty SAY statements! Two types of abuttal are used here, one using the '||' symbol and the other is achieved by typing the CONSTANT next to the literal string. We haven't seen this method of abuttal before. Note the last number in the example 711.666667, the precision here means we get a total of 9 digits. This is an environmental option which we can change with the NUMERIC keyword we'll see this later.
Operator Priorities & Brackets
These can get silly, I say that because we could spend a lot of time learning them and then writing very fancy code which works and is very clever. Clever but unreadable when two months later when you can't remember which operator has priority over its neighbour. The solution is simple use brackets even when they may not be needed! If you want to be an anorak here are the priorities, when two are the same, the priority is from left to right.
8] + - (as a prefix) 5] + - (not as a prefix)
7] ** 4] Abuttal.
Expresssions
What is an expression?
Simply its several bits of code which will be used to work out one thing, that 'thing' may be a literal string or a number. Let's take an assignment instruction for example
OUTLINE = 'The Total is =' L * 2 + W * 2 + ExtraBit
Everything here except the OUTLINE = is one expression. It contains constants, literal strings, arithmetic operators and variables but all the values will be shrunk down to one literal string that in this case will be put into the variable called OUTLINE. We could however print it by coding SAY at the beginning rather than OUTLINE =. Later we will look at functions and the arguments, which are their inputs. Normally we have just a single variable as an argument but in fact it will take a very large and complex expression. In summary an expression can be:
- Everytime to the right of a SAY instruction
- Everything to the right of the equal sign in an assignment
- Everthing between and open bracket and a comma or a close bracket in function argument
- Everything between the IF and the '=' in an IF instruction
- Everything between the '=' and the THEN in an IF instruction
- Everything after EXIT or RETURN
- Many more if you think about it.
The PULL instruction
/* REXX program to show the PULL instruction */
SAY "Program to multiple 2 numbers"
SAY "Please enter your first number"
PULL Num1
SAY "Thank you, now your second number"
PULL Num2
SAY "Well done: Now" Num1 "times" Num2 "=" Num1 * Num2
----------------------------
Program to multiple 2 numbers
Please enter your first number
12
Thank you, now your second number
14
Well done: Now 12 times 14 = 168
The PULL Instruction is a funny little baby. What it does will depend on where you are executing your program. When running it online it will pause the execution of the routine and wait for you to type something in. When executing the REXX in JCL this instruction will try and get a line from the input queue - more about that later. For now we will worry about running it online. In the example above I typed in the numbers 12 and 14 in 'answer' to the two PULL statements. Its a pretty obvious thing to say but if you are going to use PULL like this then have a good SAY instruction just before it so you will know what to type in!
There is far more to PULL than meets the eye, you can not only accept input with it but also split it up, convert it to upper case, its very clever. Refer to end of the PARSE section for more details.
1.3 Conditional Processing
Comparative Operators
/* REXX showing Comparative Operators */
SAY 12 = 12
SAY "fred" = "fred"
SAY 12 = 13
SAY "fred" = "freda"
----------------------------
1
1
0
0
Normal Comparative operators.
In the example above you can see how REXX compares two expressions. If we code 'variable-name = expression' it knows we are doing an ASSIGNMENT, but an '=' almost everywhere else means we are COMPARING two expressions. Note I say EXPRESSIONS and not TOKENS. These always return a '1' if the answer is TRUE and a '0' if the answer is FALSE. In the example above we are checking to see if two expressions are equal, here are all the comparative operators.
= means equals <> or >< or \=, means not equals
< means less than >= or \< means not less than
> means greater than <= or \> means not greater than
Note sometimes other platforms have other NOT symbols. Sometimes they use a rotated L sometimes a / (not a \).
IF THEN & ELSE Instructions
/* REXX IF program */
A = 10 ; B = 12
IF A = B THEN SAY A "IS EQUAL TO" B
ELSE SAY A "IS NOT EQUAL TO" B
----------------------------
10 IS NOT EQUAL TO 12
We can use the result of a comparative operator with an IF instruction to ensure that some instructions are only executed under certain conditions. When the comparison result is 1 (TRUE) then the statment after the THEN instruction is executed. If we want we can add an ELSE on the next line and this statement will be executed if the result is 0 (FALSE), this is optional. Note that the word ELSE has been indented. This makes the code more readable and as you will see in a minute will help you find syntax errors.
Using DO & END with the IF Instructions
/* REXX program showing DO-END blocks */
A = 76 ; B = 76
IF A = B THEN DO
SAY "A=" A "B=" B
SAY "AND THEY ARE THE SAME" ; END
ELSE DO
SAY "A=" A "B=" B
SAY "AND THEY ARE DIFFERENT"; END
----------------------------
A= 76 B= 76
AND THEY ARE THE SAME
In the last example we could only execute one single instruction conditionally. There are lots of situations where we will want to execute lots and lots of instructions. Here we have added the keywords DO and END. As it says in the text for every DO you must have an END statements.
Indenting Code
How do we ensure we always remember to code the END keywords ? The answer is all to do with indenting the code. I always line up the END statement with the other statements in the block. I've seen other peoples code where they have lined it up with the IF instruction. It doesn't really matter which you do, get into your own habit and stick with it. Now when your program failed with the error MISSING or UNEXPECTED END you can just glance through your code and see where you have missed out the END keyword.
More Complex Comparisons
/* REXX program. Comparative Operators. */
SAY "Some more complex Comparisons."
SAY "Compare ' 010 ' and '+10.00' ";SAY " 010 " = "+10.00"
SAY "Compare ' strip ' and 'strip' ";SAY " strip " = "strip"
SAY "Compare 'FRED' 'fred'" ;SAY "FRED" = "fred"
SAY "Is 'APPLE' less than 'BANANA'?";SAY "APPLE" < "BANANA"
SAY "Is 'a' greater than 'A'?" ;SAY "a" > "A"
SAY 12 < "Apple" ;SAY 12 < "Apple"
----------------------------
Some more complex Comparisons.
Compare ' 010 ' and '+10.00'
1
Compare ' strip ' and 'strip'
1
Compare 'FRED' 'fred'
0
Is 'APPLE' less than 'BANANA'?
1
Is 'a' greater than 'A'?
1
1
0
Numerical Comparisons Now you have seen how IF instructions work we (I mean you of course) are going back to look at some more complex comparisons, if you want to do some clever IF instructions you will need to go through this next bit even if its a bit heavy.
When I say complex comparisons I don't really mean complex just could-be-true could-be-false. In the first test here you can see that the result is TRUE if the numerical value of the two expressions are the same. Leading and trailing spaces are removed as well as unnecessary zeros and signs. This only occurs when both expressions are considered to be numerical and not literal strings.
Literal Comparisons
The two literal comparisons shown here also tell us a few things. First leading and trailing spaces have no effect. The comparisons are case sensitive, here "FRED" is not equal to "fred". The last two examples show us that we can see if one literal string is alphabetically greater or less than another, this is also case sensitive. It is legal to do something like "A" <12 but we are unlikely to use this in practice.
Strict Comparisons
/* REXX program. Strict Comparative Operators. */
SAY "Now we will look at STRICT comparisons."
SAY "Strickly compare ' strip ' & 'strip' "
SAY " strip " == "strip"
SAY "Check 'FRED' & 'fred' are strictly not equal"
SAY "FRED" \== "fred"
SAY "Strictly compare ' 010 ' & '+10.00' "
SAY " 010 " == "+10.00"
SAY "Strictly compare 12 * 3 & 72 / 2"
SAY 12 * 3 == 72 / 2
----------------------------
Now we will look at STRICT comparisons.
Strickly compare ' strip ' & 'strip'
0
Check 'FRED' & 'fred' are strictly not equal
1
Strictly compare ' 010 ' & '+10.00'
0
Strictly compare 12 * 3 & 72 / 2
1
Another feature of REXX is the 'strict comparisons' . Here the expressions must be exactly the same to get a 1 as the result. Leading and trailing spaces are not removed as you can see in the examples above. It is of course case sensitive as you would expect.
Numeric Strict Comparisons
This time the numbers are treated more like literal strings, it's not only their numerical value that must be the same, but also there must not be any extra characters getting in the way.
Comparison Priority
Comparisons have a priority of 3, which is very low. This means that most other operations are performed first (including concatenation / abuttal). In the last example here we can see that the multiplication and division is carried out before the comparison.
Logical Comparisons
/* REXX program. Logical (Boolean) Operators. */
1 SAY "Logical operators compare two results."
2 SAY "Print 1 if: 1 = 1 AND 2 = 2" ;SAY 1 = 1 & 2 = 2
3 SAY "Print 1 if: 1 = 3 AND 2 = 2" ;SAY 1 = 3 & 2 = 2
4 SAY "Print 1 if: 1 = 1 OR 1 = 2" ;SAY 1 = 1 | 1 = 2
5 SAY "Print 1 if: 1 = 2 OR 2 = 1" ;SAY 1 = 2 | 2 = 1
6 SAY "Print 1 if: 1 = 1 XOR 1 = 2" ;SAY 1 = 1 && 1 = 2
7 SAY "Print 1 if: 1 = 1 XOR 2 = 2" ;SAY 1 = 1 && 2 = 2
8 SAY 1 | 0
9 SAY 2 & 1
10 SAY 1 && 1
----------------------------
Logical operators compare two results.
Print 1 if: 1 = 1 AND 2 = 2
1
Print 1 if: 1 = 3 AND 2 = 2
0
Print 1 if: 1 = 1 OR 1 = 2
1
Print 1 if: 1 = 2 OR 2 = 1
0
Print 1 if: 1 = 1 XOR 1 = 2
1
Print 1 if: 1 = 1 XOR 2 = 2
0
1
+++ Error 46 in line 9: Boolean value not 0 or 1
Command returned 10/46: Boolean value not 0 or 1
Combining two results with Logical Operators
Now we really are getting clever. We are combining the results of two (can be more) results to produce a final result. The examples show the AND, OR and exclusive-OR logical operators. These can be used to combine numerals (as long as they are 0 or 1s) not just the results from other comparisons. For example the instruction SAY A & B would be legal as long as A and B have values of either 1 or 0. You can see what happens if the input is not a 1 or zero; we get an error. Note that the sign for exclusive-OR is two AND signs, not two OR signs - that means abuttal of course.
Comparison (Boolean) errors.
The line 9 of the example, it shows a very common error. We are trying to AND two values , but one of these is not a 0 or a 1. It looks pretty silly here but it's a very common error when variables are used. For example if you coded SAY 1 | A it would fail if A had not been initialized with a 0 or 1. Remember this, it may be one of dozens of things you are trying to absorb right now but its one which will come back to haunt you (and several times if its in a loop).
Logical Operators Prioritiy
The priority of the AND (&) is 2 and the priority of OR (|) and exclusive OR (&&) are both 1. This means they are done after all other operations within the expression. But as I have said before when in doubt about priority use brackets.
The 'NOT' Operator
/* REXX. The NOT operator and comparison errors. */
SAY "The NOT operator can be used to reverse a result."
SAY " 1 not= 1 will give a:" ; SAY 1 \= 1
SAY " not 1 = 1 will give a:" ; SAY \ 1 = 1
SAY "not 1 = 1 XOR 2 \= 1 gives:" ; SAY \ 1 = 1 && 2 \= 1
SAY " 1 = 1 AND 1 gives:" ; SAY 1 = 1 & 1
----------------------------
The NOT operator can be used to reverse a result.
1 not= 1 will give a:
0
not 1 = 1 will give a:
0
not 1 = 1 XOR 2 \= 1 gives:
1
1 = 1 AND 1 gives:
1
It is possible to reverse the result of a comparison by the use of the NOT operator. The symbol used for this can vary with different platforms. On TSO it is possible to use an inverted and rotated L (which I haven't got in my character set or I would show you) or a / (slash). In the examples above you can see that the NOT symbol can be part of a comparison operator or at the beginning of the expression on its own. When it is used on its own it has a priority of 8, which is very high.
The SELECT, WHEN, OTHERWISE and NOP instructions
/* REXX The SELECT, WHEN, OTHERWISE and NOP instructions */
A = "120"
SELECT
WHEN A = "100" THEN NOP /* DO NOTHING */
WHEN A = "200" THEN SAY "A IS 200"
WHEN A = "300" THEN DO
SAY "DO A BLOCK OF CODE IF ITS 300
END
OTHERWISE SAY "NONE ABOVE ARE TRUE"
END
If you are a matching socks and tie man (person) then this stuff is for you. This can tidy up the crappist code. This is wonderful take a look. If you have several IF instructions all asking the same basic question then here we can handle the situation with wonderful easy, simplicity itself and all sort of other very long words. The idea is that if the first WHEN condition is TRUE it will execute the code after the THEN statement and then go straight to the END statement without doing any of the other tests. If the condition if FALSE it will try the next WHEN condition etc. If all of these conditions are FALSE the you the instructions next to OTHERWISE. Note then you don't code a THEN next to OTHERWISE. If we want to execute a block of statements we just code a DO after the THEN and an END before the next WHEN (what a month full). It's the same as the IF instruction. When there is a condition where you don't want to execute any instructions and you want to go straight to the END you can code NOP (no - operations).
1.4 Instruction Loops
DO Loops (with FOR, BY and TO)
/* REXX. Introducing DO loops */
DO A = 1 TO 7 BY 2
SAY "A=" A ; END
DO B = 1 FOR 4
SAY " B=" B ; END
----------------------------
A= 1
A= 3
A= 5
A= 7
B= 1
B= 2
B= 3
B= 4
DO loops come is several shapes and sizes, but it's all about being able to execute a block of code more than once. It's the only way to do it in REXX so they are rather important. In these examples we are controlling the number of times we go round the loop by two different ways but on each we are incrementing a variable (which is very important). In the first example we are incrementing the variable called A. We'll keep going round the loop until A is equal to 7. The BY parameter means that each time we go round the loop A is incremented BY 2 . If BY was not coded A would default to an increment of 1. In the second example the FOR parameter has been used to ensure we go round the loop 4 times. Here B is incremented but its not B's value that will terminate the loop. To be honest you won't see this facility used a lot and I am never quite such why! What you will find is this simulated with less efficient code, where an incremented counter is added with extra statements. Very under-rated / under-used are the FOR and BY parameters.
DO WHILE and UNTIL loops
/* REXX showing UNTIL/WHILE loops*/
A = 6; B = 1; C = 1
DO WHILE A \= B
SAY "B=" B
B = B + 1 ; END
DO UNTIL A = C
SAY " C=" C
C = C + 1 ; END
----------------------------
B= 1
B= 2
B= 3
B= 4
B= 5
C= 1
C= 2
C= 3
C= 4
C= 5
It is very rare to find a REXX program without one of these two. Here we have a comparison operator and we keep going round the loop WHILE it is TRUE or UNTIL it is TRUE. There is no automatic increment so people will sometimes code there own. While the WHILE and UNTIL parameters look like exact opposites, there is one very important difference between them. The WHILE test is executed at the beginning of the loop and the UNTIL test is executed at the end - look at the red lines. See the following section for the next exciting episode where you can see the effect this can have!
The LEAVE instruction
/* REXX program to show the LEAVE instruction */
MaxNum = 10000; MaxLoop = 10 ; A = 2
DO Loop = 1 TO MaxLoop
B = A * A
SAY "Loop1=" Loop "B =" B
A = B
If A > MaxNum THEN LEAVE
END
A = 2
DO Loop = 1 TO MaxLoop WHILE A < MaxNum
B = A * A
SAY "Loop2=" Loop "B =" B
A = B
END
----------------------------
Loop1= 1 B = 4
Loop1= 2 B = 16
Loop1= 3 B = 256
Loop1= 4 B = 65536
Loop2= 1 B = 4
Loop2= 2 B = 16
Loop2= 3 B = 256
Loop2= 4 B = 65536
Two examples again, both produce the same output with differing code. The first introduces the LEAVE instruction. Very simple and very useful. You leave the loop immediately you hit this instruction. In this example we go straight the SAY instruction Here it is not really necessary to use LEAVE, this is because it is the last instruction within the loop. It is cleaner to code the test on the DO instruction, which is what we have done in the second example. Note I've been a good boy and not coded the constants within the body of the program, they are defined at the top. Strictly speaking there is a mistake here, the it should be WHILE A <= MaxNum.
The ITERATE & FOREVER instructions
/* REXX program to show ITERATE & FOREVER */
A = 0; Incr = 1; Divis = 4; MaxNum = 10
DO FOREVER
A = A + Incr
IF A = MaxNum THEN DO
SAY "HIT LOOP END CONDITION"
LEAVE
SAY "NEVER EXECUTED" ; END
IF (A // Divis) = 0 THEN DO
SAY "HERE" A "IS DIVISIBLE BY" Divis
ITERATE
SAY "NEVER EXECUTED" ; END
SAY "A=" A
END
SAY "REACHED END"
----------------------------
A= 1
A= 2
A= 3
HERE 4 IS DIVISIBLE BY 4
A= 5
A= 6
A= 7
HERE 8 IS DIVISIBLE BY 4
A= 9
HIT LOOP END CONDITION
REACHED END
The ITERATE instruction
Another new keyword to remember: ITERATE. Very straightforward. If you hit this you go straight back to the beginning of the loop. Note that here the LEAVE instruction is within a IF DO - END block, the LEAVE doesn't mean leave this block, it means leave the ,DO loop!
The FOREVER instruction
Here is another way we can do a DO! Coding DO FOREVER means we will just keep going round and round. The only escape will be via a LEAVE instruction (or an EXIT or a RETURN.)
More DO loops
/* REXX program to show other types of DO */
LineSpace = 3; Max = 10
DO LineSpace; SAY "***"; END
B = ""; A = 0; KeepGoing = 1
DO WHILE KeepGoing
A = A + 1
IF A = Max THEN KeepGoing = 0
B = B A ; END
SAY B
DO LineSpace; SAY "***"; END
----------------------------
***
***
***
1 2 3 4 5 6 7 8 9 10
***
***
***
Some more examples of DO loops. The first and third loops are printing the *** lines. Coding DO n is legal when n is a number (or a variable contains a number), it's very simple the loop is performed that number of times. I've broken the normal convention over having one instruction per line because it seemed sensible to do so!
The second loop is just showing you that the expression after the word UNTIL must have a value or 1 or 0. On most of the other examples we have used a comparison - the result of would be a 1 or 0. It's all another way of achieving the same thing. While it looks like this involves more code and therefore is probably less efficient, you will see a lot of code like this and therefore its important for you to be able to understand it.
1.5 Other bits: INTERPRET and Functions
The INTERPRET instruction
/* REXX program to show the power of INTERPRET */
A = "SAY 'Input REXX instructions or END'"
INTERPRET A
DO FOREVER
PULL Input
IF Input = "END" THEN EXIT 0
INTERPRET Input ; END
----------------------------
Input REXX instructions or END
A = 15
B = 20
SAY A * B
300
END
The first instruction here is an assignment. It's putting a complete REXX instruction into a variable, you may think this is a strange thing to do but the INTERPRET instruction can execute this! If the second part of this program doesn't knock your socks off - nothing will. It's providing you with the facility to type in any REXX command online and show you its result! Look at the entries I give the program, its executing each line at a time, in effect it is executing the program as I type it in! Type up the second part of this program and always have it at your fingertips for use as a calculator or trying REXX instructions!
Functions (Sub-routines)
/* REXX : Internal functions. */
SAY "First Line"
CALL MyFunction
SAY "Last Line"
EXIT 0
MyFunction:
SAY "Welcome to my own function!"
RETURN 0
----------------------------
First Line
Welcome to my own function!
Last Line
We are now going to talk about sub-routines, these are used very extensively used in large programs. It is said that the main routine of a program should only really contain CALL statements which invokes blocks of code where the real work is done. It helps you keep sight of what is going on without getting snarled up in the detail. The other time they are very useful is when the same piece of code is required in several places. When the code is in a function you just code it once and then have different CALL instructions. In the example above we have one homemade functions. These are always coded after the EXIT instruction. Their first line is called a LABEL instruction, it always ends with a colon. The function starts here and ends with a RETURN instruction.
The ARG instruction & RESULT variable
/* REXX program to show function communication. */
CALL Funct1 2
SAY "Give the funct1 a" 2 "and you get back a" Result
SAY "Give the funct1 a" 4 "and you get back a" Funct1(4)
EXIT 0
Funct1:
ARG Input
Code = Input * 3
RETURN Code
----------------------------
Give the funct1 a 2 and you get back a 6
Give the funct1 a 4 and you get back a 12
Look at the CALL instruction, here you can see two way communication between the calling routine and the function. The argument is put next to the function name and this is picked up by the ARG instruction in the function. The ARG instruction gets very heavy and we will deal with this in detail later in the PARSE section. Look at the line RETURN code, this returns the value of to a variable called RESULT.
The next time the Funct1 function is 'called' we are using a different syntax. Instead of using the instruction CALL we place some brackets next to the function name. Note that there must not be a gap between the function name and the opening bracket! The argument is put inside the brackets. The way the return code is handled is also different. The function becomes the result. You can then put the value into a variable or print it straight out! All clever stuff, as a general rule if you are executing a function and you want the result people tend to code FUNCTION( arguments ) but if they don't need the result they code CALL FUNCTION arguments.
Built in Functions
/* REXX : DATATYPE & LENGTH functions */
A = "string" ; B = 12.0000; C = "+12.0456"
SAY "The data type of '" || A || "' is " DATATYPE(A)
SAY "The data type of '" || B || "' is " DATATYPE(B)
SAY "The data type of '" || C || "' is " DATATYPE(C)
SAY "'" || A || "' is" LENGTH(A) "characters in length."
SAY "'" || B || "' is" LENGTH(B) "characters in length."
SAY "'" || C || "' is" LENGTH(C) "characters in length."
EXIT 0
----------------------------
The data type of 'string' is CHAR
The data type of '12.0000' is NUM
The data type of '+12.0456' is NUM
'string' is 6 characters in length.
'12.0000' is 7 characters in length.
'+12.0456' is 8 characters in length.
Here we are calling two different functions, one called DATATYPE and the other called LENGTH, but where are they? They are not listed below the EXIT 0 are they? "No" I hear you cry! That's because there is a library of built in functions that you can access which increases the power of this language immeasurably. You will see what I mean in a minute. These two aren't bad for starters. The first one DATATYPE can tell us whether the contents of a variable is a number or a string of characters. Very very useful for checking data before its going to crash your program by trying to multiple something which is not a number!
Section 2 will now going to go through the more common functions that are built in.
2. String Functions
2.1 String Alignment Functions: CENTER, CENTRE, LEFT & RIGHT
CenteredString = CENTER( Instr, Length, PadChar)
CenteredString = CENTRE( Instr, Length, PadChar)
LeftAdjustedString = LEFT( Instr, Length, PadChar)
RightAdjustedString = RIGHT( Instr, Length, PadChar)
All of these are pretty standard really. LEFT & RIGHT are normally used to ensure that a string is a particular length for printing alignment. The LEFT function can be used to see if the first characters of one string are the same as another but it is better to use the ABBREV function for that type of thing.
CENTRE('hello',14) = ' hello '
CENTER('=1234567890 =',5) = '34567' CENTER('abc',8,'-') = '--abc---'
LEFT('123456789',12) = '123456789 ' RIGHT('1223456789',12,'-')= '---123456789'
LEFT('123456789',4) = '1234' RIGHT('123456789',4) = '6789'
Note that you can use either the word CENTRE or CENTER it doesn't matter. It hardly needs saying but in all of these functions (and loads of others) the default pad character is a space. This pad is used when the source string is shorter than the length. If the CENTRE can not get it exactly right because there is an odd number of spaces, the leading gap is smaller.
2.2 String Comparison Functions: COMPARE & ABBREV
DiffCharNo = COMPARE(String1, String2 , PadChar)
Boolean = ABBREV( LongString, ShortString , MinLength)
These two are doing exactly what they say they are: COMPARE compares two strings while ABBREV checks that one string is an abbreviation of the other. Note the odd purpose of the last argument on ABBREV, it checks that the abbreviated string is of a particular length! If it's not the function will always return a 0.
COMPARE('ABC','ABC') = 0; ABBREV('abcdefghi','abcd') = 1
COMPARE('ABCD','ABC') = 4; ABBREV('abcdefghi','abzz') = 0
COMPARE('ABC','ABCD') = 4; ABBREV('abcdefghi','abcd',6)= 0
COMPARE('ABCD','ABcD') = 3; ABBREV('abcdefghi','abcd',4)= 1
COMPARE('WXYZ ','WXYZ') = 0;
COMPARE('ABC---','ABC-','-')=0
Note that COMPARE is returning the position of the first mis-match while ABBREV is returning a Boolean 0 or a 1. On the second to last example ABBREV has returned a '0' because the second string is not 6 characters in length!
2.3 String Combining Functions: OVERLAY & INSERT
BothString= INSERT( StringtobeInserted, BaseString, at, Length, Pad)
String = OVERLAY( OverlayingString, BaseString, at, Length, Pad)
These two are used to put all or part of one string into another. The difference between the two functions is obvious INSERT doesn't over type any of the second string. Again we have a pad character (the default is a space) if the our string is less than the length we want to insert or overlay.
INSERT('12345','abcdef') = '12345abcdef'
INSERT('12345','abcdef',3) = 'abc12345def'
INSERT('12345','abcdef',3,2) = 'abc12def'
INSERT('123','abcdef',2,5,'*') = 'ab123**cdef'
OVERLAY('12345','abcdefghi') = '12345fghi'
OVERLAY('12345','abcdefghi',3) = 'ab12345hi'
OVERLAY('12345','abcdefghi',3,2) = 'ab12efghi'
OVERLAY('123','abcdefghi',2,5,'*')= 'a123**ghi'
The most confusing thing here is the order of the first two strings, from these examples you can see that it's the 2nd argument, which is overlaid/inserted into the 1st argument.
I use OVERLAY a lot when creating output lines. I set a line to be blank and then use OVERLAY to put into the output the variables at the required column. This may not be very efficient, but if that doesn't matter too much, you will find it very easy to built up an output line and modify it later. For example:
OUT = ""
OUT = OVERLAY( TimeVar , OUT , TIMECOLUMN )
OUT = OVERLAY( DateVar , OUT , DATECOLUMN )
OUT = OVERLAY( NameVar , OUT , NAMECOLUMN )
SAY OUT
2.4 String Extraction Functions: SUBSTR & DELSTR
Sub-string = SUBSTR( String, StartofBitRequired, Length)
Sub-string = DELSTR( String, StartBittoBeLeftOut,Length)
These functions are all to do with extracting part of string and putting it into another. DELSTR will delete a section of a string while SUBSTR does the opposite and extracts a section. You will find you hardly ever use DELSTR and use SUBSTR all the time With DELSTR if you don't code a length it will delete from the start point to the end of the string. With SUBSTR not coding a length means you get everything from the start position and the end of the string. When we say 'delete a section of the string' we don't mean that 'InStr' is change.
DELSTR('12345678',5)= '1234' DELSTR('12345678',4,2) = '123678'
DELSTR('abcdefgh',12) = 'abcdefgh' SUBSTR('12345678',4) = '45678'
SUBSTR('12345678',4,2) = '45' SUBSTR('12345678',14,2) = ' '
SUBSTR('12345678',5,7) = '5678 ' SUBSTR('12345678',5,7,'-')= '5678---'
As usual here are some amazing examples. Look at the first one, note that '5' is not in the returned string because it is the first character of the block to be deleted. Compare this with the first SUBSTR example where we do get a '4' because this is the first character of the sub-section that we require. Note if you find you need more than one SUBSTR instruction when getting different bits out of the same string, you should consider using PARSE VAR instead!!
2.5 String Search Functions: INDEX, POS & LASTPOS
PosNum = POS( StringtobeFound, StringToLookIn, Start)
PosNum = INDEX( StringToLookIn , StringtobeFound,Start)
PosNum = LASTPOS( StringtobeFound, StringToLookIn, Start)
These all search one string (the 'StingToLookIn' string) to see if it contains another (the 'StringToBeFound' string). POS and INDEX are the same but the 1st and 2nd arguments are coded in the reverse order. With any of these functions coding a 3rd argument limits the area searched. They return the start position within the 'target' string, which matches the 'find' string. LASTPOS searches from the right hand end of the 'target' string but it still returns the position from the front of the string.
POS('x', '1234567x90ABCDxFG') = 8 POS('xx','1234567xx0ABCDxxG',10)= 15
POS('z', '1234567x90ABCDxFG') = 0 INDEX('1234567xx0ABCDxxG','xx') = 8
LASTPOS('x','1234567x90ABCDxFG')= 15 LASTPOS('x','123x567x90ABCDxFG',10)= 8
Here are some examples to have a look at. Note that the functions return a '0' if the string is not found. Some platforms do not support both POS and INDEX (my Amiga does of course) but obviously this is not a headache. If you are looking for a single word (i.e. some letters with spaces either side) then you many want to use the WORDPOS function although this returns the word number not its position.
2.6 Other String Functions
2.6.1 The COPIES & RANDOM Functions
LongOutString = COPIES( StringTobeRepeated, NumberRequired)
RandomNumber = RANDOM( LowestNumber, HighestNumber )
COPIES is used to multiple a string a number of times. It is far more useful than it sounds. If you wish to output lines of dashes or asterisks then use this function to generate them with a PageWidth variable in the second argument. That way when (not if) you decide you want longer or shorter lines you change one constant at the top.
There is nothing odds about them worth mentioning, may be just that while on the Amiga only the first character of StringTobeRepeated is used on TSO its the whole string! Below is a near little program COPIES and RANDOM can be used.
/* REXX showing COPIES and RANDOM functions */
Array. = 0 ; PageWidth = 44
DO UNTIL Array.RandNum = (PageWidth - 8) /* DO TIL WEVE HAD ONE NO. 36 times */
RandNum = RANDOM(1,10) /* GET RANDOM NUMBER BETWEEN 1 -10 */
Array.RandNum = Array.RandNum + 1 ; END /* COUNT NO.OF TIMES WE GOT THIS NO.*/
SAY COPIES("*", PageWidth) /* PRINT LINE OF STARS */
DO X = 1 TO 10 /* PRINT NO.OF TIMES WE GOT RANDOM..*/
SAY RIGHT(X,2) RIGHT(Array.X,2) COPIES("*", Array.X )
END /* ...NO. + LINE FOR BAR GRAPH */
SAY COPIES("*", PageWidth) /* PRINT LINE OF STARS */
-----------------------------------------------------------------------
********************************************
1 23 ***********************
2 20 ********************
3 14 **************
4 17 *****************
5 9 *********
6 36 ************************************
7 26 **************************
8 22 **********************
9 22 **********************
10 25 *************************
********************************************
2.6.2 The LENGTH Function
LengthofString = LENGTH( String )
If you need help working out what this one can do its pretty sad. However one of the examples may give you a surprise, look at the last one!!!!
LENGTH(123456789) = 9 LENGTH(") = 0
LENGTH(ABCDEFGHIJKLM) = 13 LENGTH(COPIES("*",50)) = 50
LENGTH("12.000000000") = 12
2.6.3 The REVERSE string function
ReversedString = REVERSE( StringToBeReveresed )
This is a very silly function, I am sure there is an anorak who thinks that one day they may want to reverse the contents of a string but who else!
REVERSE('abcdefghijk') = 'kjihgfedcba'
2.6.4 The STRIP function
StrippedString = STRIP( StringToBeStripped , Options, Char )
This is very useful, it is usually used to ensure you have no spaces at the being or end of a string, however it can be used just to clear leading or trailing spaces or in fact any other characters.
STRIP(' remove spaces ') = 'remove spaces'
STRIP(' leading only ','l') = 'leading only '
STRIP(' trailing only ','t') = ' trailing only'
STRIP(' both (default) ','b') = 'both (default)'
STRIP('---remove dashes---','b','-') = 'remove dashes'
2.6.5 The TRANSLATE function
OutStr = TRANSLATE(Instr, TranO, TranI, Pad )
There are two purposes for this function, one simple, one not. If you just code one argument then all lower case characters in 'InStr' are converted to capitals! That's the simple one use. If 3 or 4 arguments are given then the conversion (translation) is more complex. The functions takes the 1st character in 'TranI' and hunts through 'InStr' and tries to finds all occurrences of this, these are changed into the first character in the 'TranO' string. In then hunts for the any occurrence of the 2nd character in 'TranI' and converts this to the second character in 'TranO' . It keeps going until all characters in 'TranI' have been converted. If it runs out of characters in 'TranO' then all remaining character in 'TranI' are converted to the pad character.
TRANSLATE('abcdefghi') = 'ABCDEFGHI'
TRANSLATE('abcdef','123','abc') = '123def'
TRANSLATE('abcdef','1','abcd','*') = '1***ef'
TRANSLATE('No Exclaiming! Or questions?','','!?','.')
= ' No Exclaiming. Or Questions.'
This starts off being quite sensible and ends up being heavily hairy. The first example shows a straightforward conversion to upper case characters. Look at the second example. This is converting any 'a' characters to '1's, any 'b's to '2's and any 'c's o '3's. All other characters are left alone - they are not even changed into upper case. The third example shows what happens when you add a 4th argument and the 2nd is shorter than the 3rd. Now any 'a's are converted into '1's but there are no character to correspond with b, c or d. Now that take the value of the 4th argument! A semi-practical example is shown last. Here we are converting all '!' and '?' to '.'.
2.6.6 The VERIFY function
Number = VERIFY(Instr, RefStr , Opt, Start )
VERIFY can be used to check that all the characters in a 'target' string are in another 'reference' string. If there are no rogue character it returns a 0, if there are it's the position of the first bad one.
VERIFY('4567','1234567890') = '0'
VERIFY('45x7','1234567890') = '3'
Here are some examples. The first checks to see if the string '4567' just contains any character between 0 and 9, as it does the result is a zero. In the second example the 'x' means the result is 3, the position of the 'x' .
2.6.7 The XRANGE function
XRANGE is an odd beast it will return a string which contains all the characters within an ASCII range - It can be use to build the reference strings needed for VERIFY. If you don't understand all this don't loose any sleep over it. We can use XRANGE with VERIFY like this:
VERIFY('456e76',XRANGE('0','9')) = '4'
VERIFY('somechar',XRANGE('a','z')) = '0'
VERIFY('some char',XRANGE('a','z')) = '5'
Three of the examples show how we can build the reference string required for VERIFY with XRANGE. This could be much easier if it's a very long range.
3. 3. Other Functions
3.1.1 The WORD function
TheNthWord = WORD(InputString, NoOfWordRequired )
Highly simple this, the WORD function always returns one word of an input string, which word will depend on the number given in the second argument.
WORD('one two three four five',3) = 'three'
WORD('one two three four five',1) = 'one'
WORD('one two three four five',6) = ''
Not a lot more to say really except remember there is also a WORDS function (returns the number of words in a string) and its easy to get the two confused.
3.1.2 The WORDINDEX function
PositionofWordNumber = WORDINDEX( InputString, WordNumber )
This function returns the position of a word!!! You can see from the examples below the position is the character position.
WORDINDEX('one two three four five',3) = '9'
WORDINDEX('one two three four five',4) = '15'
Don't get this function confused with WORDPOS , which is searching a string for a word.
3.1.3 The WORDPOS function
WordNumber = WORDPOS( FindThisWord, InThisString, StartWordNo )
This function searches a string for a word and returns the word number if the word is found.
WORDPOS('four','one two three four five' ) = '4'
WORDPOS('five','one two three four five',3) = '5'
Don't get this function confused with WORDINDEX function.
3.1.4 The WORDLENGTH function
LengthOfWord = WORDLENGTH( StringToLookIn , WordNo )
This function returns the length of a word within a string, the second argument gives the number of the word for which we require the length.
WORDLENGTH('one two three four five',3) = '5'
WORDLENGTH('one two three four five',5) = '4'
Don't get this function confused with WORDINDEX function.
3.1.5 The SUBWORD function
PartOfInString = SUBWORD( InString, FirstWordNo, NoOfWords )
This function extracts part of string, the second argument gives the first word to be copied and the third argument gives the no of words to be copied.
SUBWORD('one two three four five',3) = 'three four five'
SUBWORD('one two three four five',3,2) = 'three four'
You may be better off using SUBSTR which works on character positions and character length it all depends on what you are doing.
3.1.6 The DELWORD function
MostOfString = SUBWORD(String, FirstWordNotCopied , NoOfWordsNotCopied )
This function copies the whole string except a block defined by the 2nd and 3rd arguments. The 2nd argument is the number of the first word not to be copied and the 3rd argument (if coded) is the number of words not to be copied. If the 3rd argument is not coded all the remaining words are not copied.
DELWORD('one two three four five',3) = 'one two '
DELWORD('one two three four five',3,2) = 'one two five'
3.1.7 The WORDS function
NoOfWords = WORDS( String )
This very simply tells you have many words are in a string!
WORDS('one two three four five') = '5'
WORDS('' ) = '0'
Don't get this function confused with WORDS!
3.1.8 The SPACE function
SpacedString = SPACE(String, NoOfSpaces , PadChar )
In its simplest form this function will output a string with only one space between each word. However to get this to happen you must give the 2nd argument a value of 1. Its default value is zero, which will remove all the spaces. If a 3rd argument is coded, this is the character between the words (the default being a space of course)
SPACE(' one two three four ') = 'onetwothreefour'
SPACE(' one two three four ',1) = 'one two three four'
SPACE(' one two three four ',3,'_') = 'one___two___three___four'
3.2 Number Functions(and instructions)
3.2.1 The TIME function
CurrentTime = TIME( Option )
Options:
'N' = hh:mm:ss (default) 'H' = hh (whole since midnight)
'L' = hh:mm:ss.uuuuuu 'M' = mm (whole since midnight)
'R' = Reset Timer 'S' = ss (whole since midnight)
'E' = Elasped time since reset 'C' = hh:mmAM/PM
The little baby has two uses, the first obvious one is to tell you what the time is on any of the formatted detailed above. It can also be used as a timer which can be very useful as well as a bit of fun. Code TIME('R') near the beginning of a block of code and SAY "elapsed time=" TIME('E') after it. Record the time and then try changing the code, use different functions of many be different loop options. Record the time again and see if you change has altered the speed of your code. Sometimes the results are quite surprising.
TIME() = '20:04:41'
TIME('N') = '20:04:41'
TIME('C') = '8:04PM'
TIME('H') = '20' -completed hours from midnight.
TIME('M') = '1204' -completed mins. from midnight.
TIME('S') = '72281' -completed secs. from midnight.
TIME('R') = '0.00' - Start timer.
DO X = 1 TO 1000: END
TIME('E') = '0.006' -seconds since reset.
If you want the split up the current time considering using PARSE with an instruction like this:
PARSE VALUE TIME() WITH hhVar ':' mmVar ':' ssVar
3.2.2 The DATE function
TodaysDate = DATE( Option )
Options:
'N' = dd mon yyyy (default) 'E' = dd/mm/yy
'B' = days from 01/01/0000 'U' = mm/dd/yy
'D' = Julian Date 'O'= yy/mm/dd
'J' = Julian Date 'S' = yyyymmdd
'W' = Weekday 'M' = Month
Here WYSIWYG. Note that I have found the 'J' option doesn't work on all platforms.
DATE() = '02 Nov 1997'
DATE('N') (Normal) = '02 Nov 1997'
DATE('E')(European) = '02/11/97'
DATE('U') (USA) = '11/02/97'
DATE('O') (Ordered) = '97/11/02'
DATE('S') (Sorted) = '97/11/02'
DATE('D') (Julian) = '306'
DATE('M') (Month) = 'November'
DATE('W') (Weekday) = 'Sunday'
DATE('B') (Base) = '729695'
Don't try and work out the 'B' unless you understand all the rules for leap year and you now your date-alignment history!!
3.2.3 NUMERIC instruction, DIGITS & FUZZ functions
Numeric Precision
The program below shows how you can vary the precision of numbers and also the precision of numerical comparisons. The instruction N UMERIC DIGITS will set the precision as you can see here. The maximum is platform specific. D IGITS() will return the current setting.
The F UZZ keyword gives the reduction of precision when performing numerical comparisons. F UZZ() will return the current setting. The default is zero.
/* REXX To show DIGITS & FUZZ Functions */
SAY "Current Precision =" DIGITS() "so 22 / 7 =" 22 / 7
NUMERIC DIGITS 6
SAY "After 'NUMERIC DIGITS 6'"
SAY "Now Precision = " DIGITS() "now 22 / 7 =" 22 / 7
A = 1.11144 ; B = 1.11122
SAY "DIGITS FUZZ Prec- __A__ __B__ "
SAY " ( ) ( ) ision / \ / \"
DO MyFuzz = 0 TO DIGITS() - 1
NUMERIC FUZZ MyFuzz
Prec = DIGITS() - MyFuzz
UScore = LEFT(COPIES(" ",Prec+1) || COPIES("_",FUZZ()) ,7)
SAY COPIES(" ",17) UScore UScore
Text = " " DIGITS() " " FUZZ() " " DIGITS() - FUZZ() " " A B
IF A = B THEN SAY Text "- considered the same."
ELSE SAY Text "- considered different."; END
-----------------------------------------------
Current Precision = 9 so 22 / 7 = 3.14285714
After 'NUMERIC DIGITS 6'
Now Precision = 6 now 22 / 7 = 3.14286
DIGITS FUZZ Prec- __A__ __B__
( ) ( ) ision / \ / \
6 0 6 1.11144 1.11122 - considered different.
_ _
6 1 5 1.11144 1.11122 - considered different.
__ __
6 2 4 1.11144 1.11122 - considered different.
___ ___
6 3 3 1.11144 1.11122 - considered the same.
____ ____
6 4 2 1.11144 1.11122 - considered the same.
_____ _____
6 5 1 1.11144 1.11122 - considered the same.
3.2.4 Exponential Notation functions
REXX supports two formats of exponential notation. The default is SCIENTIFIC which has one digit ( 'power of ten' value) before the decimal point, or you can choose ENGINEERING where the number before the decimal point is always a multiple of 3. Numbers are displayed in exponential format when they get too larger or too small. Note the 'E' can be in any case.
/* REXX To show the FORM Function */
NUMERIC DIGITS 5
SAY 'After "NUMERIC DIGITS 5" then DIGITS() =' DIGITS()
DO A = 3 TO 6
SAY "1.234 * 10 **" A "=" 1.234 * 10 ** A ; END
NUMERIC FORM SCIENTIFIC
SAY 'After "NUMERIC FORM SCIENTIFIC" then FORM() =' FORM()
DO 2 ;
DO A = 3 TO 6
SAY "1.234 * 10 **" A "=" 1.234 * 10 ** A ; END
IF FORM() = 'SCIENTIFIC' THEN DO
NUMERIC FORM ENGINEERING
SAY 'After "NUMERIC FORM ENGINEERING" then FORM() =' FORM()
END; END
-----------------------------------------------
After "NUMERIC DIGITS 5" then DIGITS() = 5
1.234 * 10 ** 3 = 1234.0
1.234 * 10 ** 4 = 12340
1.234 * 10 ** 5 = 1.234E+5
1.234 * 10 ** 6 = 1.234E+6
After "NUMERIC FORM SCIENTIFIC" then FORM() = SCIENTIFIC
1.234 * 10 ** 3 = 1234.0
1.234 * 10 ** 4 = 12340
1.234 * 10 ** 5 = 1.234E+5
1.234 * 10 ** 6 = 1.234E+6
After "NUMERIC FORM ENGINEERING" then FORM() = ENGINEERING
1.234 * 10 ** 3 = 1234.0
1.234 * 10 ** 4 = 12340
1.234 * 10 ** 5 = 123.4E+3
1.234 * 10 ** 6 = 1.234E+6
In the program above you can see how the exponential format can be changed. Note how the NUMERIC DIGITS instruction can be used to alter the length a number needs to be before the exponential format is invoked. The usual default is 9 but the D IGITS() function will always tell you what the value is.
3.2.5 The FORMAT Function
The format function can be used to ensure a number is in a specific format.
FormatedNumber = FORMAT( Number, Digits, DecimalDigits )
The 2nd argument gives the number of places to the right of the decimal point, the 3rd digit gives the number of spaces after the decimal point.
FORMAT(' 123.456789',4,4) = ' 123.4567'
FORMAT('12.34',5,4) = ' 12.3400'
Note that the number is always padded out with spaces if necessary. Note that sometimes it may be better to use TRUNC function.
3.2.6 The TRUNC Function
This function can be truncate a number so it has no (or only limited number of) decimal places.
TruncatedNumber = TRUNC( Number, DecimalDigits )
The 2nd argument can give the number of places to the right of the decimal point,
TRUNC(' +0123.45678 ') = '123'
TRUNC(' +0123.45678 ',2) = '123.45'
Note that the number is always padded out with spaces if necessary. Note that it may be better to use FORMAT if you want to ensure the number is a specific length for output alignment.
3.2.7 The SIGN Function
This function will return either 1, -1 or 0 depending on whether the number is positive, negative or zero.
SignOfNumber = SIGN( Number )
The function will fail if the input is not a number.
SIGN('+12.7') = '1'
SIGN('0 ') = '0'
SIGN('-235') = '-1'
3.2.8 The ABS Function
No, not some system to stop your program (joke), this function will always return a positive number. Negative numbers are converted to positive.
AbsoluteValue = ABS( Number )
The function will fail if the input is not a number.
ABS('-057') = '57'
ABS(' 0195') = ' 0195'
3.2.9 The MIN & MAX Functions
These return either the highest or lowest number in a list.
LargestNumber = MAX( Number , Number , Number , etc )
SmallestNumber = MIN( Number , Number , Number , etc )
Note that the numbers are separated by commas
MAX( 1,2 ,3 ,4, 5, 6, 7 ) = '7'
MIN( 1,2 ,3 ,4, 5, 6, 7 ) = '1'
Here's a program using MIN , MAX and RANDOM. Note that I've had to use INTERPRET because of the commas between the numbers.
/* REXX Number functions */
NumString = RANDOM(1,50)
DO A = 1 TO 7
NumString = NumString || "," RANDOM(1,50)
END
SAY 'Numstring =' NumString
INTERPRET "SAY 'Highest =' MAX(" NumString ")"
INTERPRET "SAY 'Lowest =' MIN(" NumString ")"
-------------------------
Numstring = 25, 42, 28, 42, 10, 21, 23, 22
Highest = 42
Lowest = 10
3.2.10 The DATATYPE Function
This is a real goodie, it can be used two ways. One way gets used all the time (the second example here), this is when you just code one argument. You would use this when you don't know what a string contains, for example just after a PULL instruction. The function will either return NUM if the string contains a valid number or CHAR if it's not a number. Coding a second argument means the function will return a boolean number. '1' if the data in the input string matches the type you are looking for or '0' if it doesn't. Note the fact you can look for a whole number, when checking data this may be better than seeing if the result is 'NUM' . These are the options you can code.
either BooleanNo = DATATYPE( String , Option )
or CHARorNUM = DATATYPE( String )
Options:
A - Alphanumeric (A-Z,a-z,0-9) N - Valid Number
B - Binary only (0,1) U - Uppercase only (A-Z)
L - Lower case only(a-z) W - Whole Number
M -Mixed case (A-Z, a-z) X - Hexadecimal (0-9, A-E)
S - Valid REXX Symbol(A-Z,a-z, 0-9 ! ? _ .)
Some Examples:
DATATYPE('+123.4567') = 'NUM'
DATATYPE('This is not a number.') = 'CHAR'
DATATYPE('Only4AlphanemericChars','a') = '1'
DATATYPE('No spaces or specials!','Alphanum') = '0'
DATATYPE(SPACE('Include Spaces',0),'A') = '1'
DATATYPE('onlylowercasechars','l')(Lowercase) = '1'
DATATYPE('MixedCaseCharacters','m')(Mixedcase)= '1'
DATATYPE('UPPERCASECHARACTERS','u')(Uppercase)= '1'
DATATYPE('Valid_REXX_Symbol','s) (Symbol) = '1'
DATATYPE('Not a Valid Symbol','s') (Symbol) = '0'
DATATYPE('010101110010111010111','b')(Binary) = '1'
DATATYPE('A3b6C35689e2A9C4c905fff','x') (Hex) = '1'
DATATYPE('+123456.7890000000','n') (Number) = '1'
DATATYPE('+123456','w') (Whole number) = '1'
3.3 The CONVERSION Functions
These functions create a new string that represents the original string in a different format. Note: I've notice some platforms offer the functions X2B and B2X rather than C2B and B2C as shown here. Fancy having a function called X2C !!!
C = Character, D = Decimal, X = Hexadecimal, B = Binary
C2D('A') = '65' C2D('AB') = '16706'
C2D('0A'x) = '10' C2X('A') = '41'
C2X('AB') = '4142' C2X('0A'x) = '0A'
X2C('41') = 'A' X2C('4142') = 'AB'
D2C('65') = 'A' D2C('16706')= 'AB'
X2D('41') = '65' X2D('4142') = '16706'
X2D('41'x) = '10' C2B('A') = '01000001'
B2C('01000001') = 'A'
3.4 BIT Functions
System programmers love these bits of bit functions. I've have noticed some variations on different platforms.
C2B(BITOR('00010001'b,'01010101'b)) = '01010101'
C2X(BITOR('11'x,'55'x)) = '55'
C2B(BITXOR('00010001'b,'01010101'b)) = '01000100'
C2X(BITXOR('11'x,'55'x)) = '44'
C2B(BITAND('11110000'b,'00111100'b)) = '00110000'
C2X(BITAND('F0'x,'5C'x)) = '50'
3.5 Programming Functions and Instructions
3.5.1 The SIGNAL instruction / CONDITION function
CALL ONorOFF condition NAME trapname
SIGNAL ONorOFF condition NAME trapname
CONDITION( Option ) = ConditionInfo
If you hit an un-recoverable error in your program you may want to use this to jump to a routine which is going to detail all the horrible things that have just happened (to create the error) before ending execution. The advantage of coding it in a separate routine is that it gets all the messages out of the main body of the program and also it may get called from one of several instructions. The S IGNAL ON condition can be used to say when you should jump to your error routine and what the routine is called (trapname). The 'condition' can be:
ERROR - external command error FAILURE - external command failure.
HALT - on external halt request. NOVALUE - using an un-initialized variable.
NOTREADY - i/o stream error SYNTAX - syntax or execution error.
The difference between S IGNAL and C ALL is that S IGNAL will not return you from the function it acts as a G OTO. Note that some of the conditions can not be used with C ALL. The C ONDITION function can be placed in an error sub-routine and it will give you some useful data about the error. Without an option it will say whether a CALL or a S IGNAL (not very exciting) involved the routine. An Option of ' C' returns the condition name, for example ' ERROR' or' N OVALUE'. An option of ' D' gives the type of trap. An option of ' I' returns ' SIGNAL' or ' CALL' depending on how the routine was requested. An option of ' S' returns the status of the trapped condition.
3.5.2 The SIGL variable & SOURCELINE function
LineNoOfCALLorSignal = SIGL
SOURCELINE( LineNum ) = LineOfYourProgram
SOURCELINE() = TotalLines
When you execute an error function it may be useful to know which line of your program invoked the error which created the jump to the function. The variable SIGL is defined for you and gives you the line number. The SOURCELINE function can be used to print out that line of code. See the program in the next section for an example.
3.5.3 The RC variable & ERRORTEXT function
ErrorType = RC
ERRORTEXT( ErrorNumber ) = ErrorDescription
The RC variable is another one set for you and it gives the error code number. Unless you are very clever (in wish case you wont be reading this) this number is not much good but feed it as the argument to ERRORTEXT and it will give you a description of the error. See the example here:
/* REXX Showing the SIGNAL instruction */
1 SIGNAL ON SYNTAX
2 My_Result = 0
3 IF MyResult THEN SAY "HELLO"
4 EXIT 0
5 SYNTAX:
6 SAY "*** SYNTAX / EXECUTION ERROR******"
7 SAY "Line Number:" SIGL
8 SAY "The Line read '" || SOURCELINE(SIGL)"'"
9 SAY "The Error was:'" || ERRORTEXT(RC)"'"
10 SAY "(Total Lines): " SOURCELINE()
11 SAY "************************************"
12 RETURN 0
OUTPUT----------------------------------------------
*** SYNTAX / EXECUTION ERROR******
Line Number: 3
The Line read 'IF MyResult THEN SAY "HELLO"'
The Error was:'Boolean value not 0 or 1'
(Total Lines): 16
************************************
3.5.4 The VALUE function
ValueOfInString = VALUE( InString , NewValue )
This is similar to the INTERPRET instruction. The template above InString is a variable that would contain the name of a 2nd variable. The function would return the contents of this second variable.
/* REXX program to show VALUE() */
A = 23
B = A
SAY VALUE(B)
--------------------------------
23
3.5.5 The SYMBOL function
VARorLITorBAD = SYMBOL( InString )
I've never found this particularly useful! It will tell you if the contents of Instring is a valid variable name by returning V AR, a valid symbol name by returning LIT or BAD if its neither.
The PARSE Editor
PARSE VAR InVar Out1Var Out2Var Out3Var
where: InVar = String to be split
Out1Var = 1st word of InVar
Out2Var = 2nd word of InVar
Out3Var = All the Rest of InVar
PARSE VAR InVar Out1Var . Out2Var
where: InVar = String to be split
Out1Var = 1st word of InVar
. = Loose 2nd Word
Out3Var = All the Rest of InVar
How PARSING works!
If you ask REXX programmer to explain PARSING they normally tell you to come back when you have 3 weeks to spare, as a result people get frightened of it all. I have to admit it's more involved than other areas, but it's not really more complex. In these examples you can see we are splitting up the contents of one variable 'InVar' and putting the bits in other variables. Note that the contents of this first variable ('InVar') is not changes. In the first template 'Out1Var' and 'Out2Var' take the first and second words of 'InVar', The last variable gets the remainder of 'InVar'. If we want to 'loose' a word just put in a full stop instead of a variable name. This is what we have done in the second template.
PARSE VAR InVar Out1Var "," Out2Var
where: InVar = The Input Variable
Out1Var = Gets everything before first comma
"," = the pattern marker
Out2Var = Gets everything after first comma
PARSE VAR InVar Out1Var (In2Var) Out2Var
where: InVar = Contains the string to be split
In2Var = contains the pattern marker
Out1Var = gets everything before pattern marker
Out2Var = gets everything after pattern marker
Pattern Markers
In the last bit a string was split on spaces. this is the default pattern marker, here in the first template we have used a comma instead. So if we code a string between two variables then we split at the point where we find a match for the string. This string can be of any length. In the second tenplate the string we want to split at is coded in a vartiable, so somehow we have to tell the PARSE editor that the variable contains a pattern marker and is not an output variable. As you can see this is achieved by putting the variable in brackets.
If A = ' PARSE can split up words.'
After: 'PARSE VAR A B C D'
B = 'PARSE' C = 'can'
D = ' split up words.'
If A = ' Use a fullstop as a dummy variable.'
After: 'PARSE VAR A B . C '
B = 'Use'
C = ' fullstop as a dummy variable.'
If A = 'can split at pattern markers.'
After: 'PARSE VAR A B 'at' C'
B = 'can split ' C = ' pattern markers.'
If A = 'The "pattern marker" now in a variable.' and PatMark= '"'
After: 'PARSE VAR A B (PatMark) C (PatMark) D'
B = 'The ' C = 'pattern marker'
D = ' now in a variable.'
PARSE Examples
Here are some PARSE examples. Note that leading and trailing spaces are being stripped off. It also might be worth saying here that there may be very slight differences on stripping on different platforms, do a few tests to find out.
PARSE VAR InVar Out1Var 23 Out2Var
where: InVar = Input variable
Out1Var = gets columns 1 -22 of InVar
23 = Split Position
Out2Var = gets 23 to end of InVar
PAR VAR InVar . ":" Out1Var +8 .
where: InVar = input variable
. = loose everything to the first colon
":" = pattern marker
Out1Var = starts ":" for 8 characters
+8 = length of previous varible (Out1Var)
. = dump the rest
Numerical Pattern Markers
Now the pattern markers are numbers and not strings. In the first template we can see that coding a number indicates a split position. The previous variable (Out1Var) gets everything up to this position and the next variable (Out2Var) starts at this point. The second template shows that when a number has a plus sign it means that the number is the length of the previous variable. In this 2nd template the result would be the same if the second full stop was not coded.
PARSE VAR InVar Out1Var =(In2Var) Out2Var 20 .
where: InVar = Variable containing a string
Out1Var = Gets the first bit of InVar
=(In2Var) = The contents of In2Var is the split point
Out2Var = Get 20 characters from passed split point
20 = The length of Out2Var
. = Dump the rest
PARSE VAR InVar Out1Var +(InVar2) .
where: InVar = variable containing a string
Out1Var = firstr bit of InVar
+(InVar) = the contents of In2Var is the length of Out1Var
. = dump the rest
Numerical Position Pattern Markers in Variables.
In both these templates numerical values are not defined as constants but in variables. In the first the equal sign and the brackets mean the contents of 'In2' determining the split point. Its value must be a zero or a positive integer number . In the second template the plus sign means that the value of the variable is the length of the previous variable (here 'Out2' ).
For all of these 'A' = '123456789abcdefghij'
After: 'PARSE VAR A 3 B 8 C'
A = '12'
B = '34567'
C = '89abcdefghij'
After: 'PARSE VAR A 3 B +8 C'
A = '12'
B = '3456789a'
C = 'bcdefghij'
If Pos= '4'
and Len= '6'
After: 'PARSE VAR A =(Pos) B +(Len) C'
A = '123'
B = '456789'
C = 'abcdefghij'
PARSE Examples - 2
Here are some more PARSE examples showing positional pattern markers. No stripped is done on these of course. You can mix positional and literal pattern markers.
PARSE VAR InVar Out1Var Out2Var =(Out1Var) .
where: InVar = variable containing a string
Out1Var = the first word of Invar
Out2Var = the next part og InVar
=(Out1Var) = Out1Var holds the length of Out2Var
. = dump the rest
PARSE VAR InVar Out1Var 15 Out2Var 1 Out3Var
where: InVar = variable containing a string
Out1Var = gets the first 14 chars. of InVar
15 = split position
Out2Var = gets 15 to end of InVar
1 = split position
Out3Var = whole of inVar
Complex PARSE instructions
Life's getting slightly more complex here. In the first template the value of the positional pattern variable is extracted from the input string just before its used to determine the length of the next variable! The second template details what happens when a numeric positional pattern has a value less than a previous positional pattern - it just goes back to that position. In the example here we go back to the 1st character so 'Out3Var' would be the same as 'InVar'.
PARSE VALUE "a long string" WITH Out1Var Out2Var etc
where: "a long string" = the input to PARSE
Out1Var etc = output variables and pattern markers
PARSE VALUE funct() WITH Out1Var etc
where: funct() = function result
Out1Var etc = output variables and pattern markers
PARSE using VALUE & WITH
In the last templates the input to the editor could only be a variable. Using VALUE and WITH the input can be just about anything. In the first template here we are splitting up a literal string constant that has been coded. To be honest you are unlikely to want to do this in real life, however you will want to do what is shown in the second template. Here we are splitting up the return from a function. Anything after the word 'WITH' are either the output variables or pattern markers. This produces a very powerful feature.
/* REXX. PARSE Editor continued */
SAY "If TIME() = '" || TIME() || "'"
SAY ' After: ''PARSE VALUE TIME() WITH hh ":" mm ":" ss'''
PARSE VALUE TIME() WITH hh ":" mm ":" ss
SAY " hh = '" || hh || "'"
SAY " mm = '" || mm || "'"
SAY " ss = '" || ss || "'"
A = "one two three four five six seven"
SAY "A = ""one two three four five six seven"""
SAY "If you keep executing: 'PARSE VAR A . A'"
SAY "THEN..."
DO WHILE A \= ""
SAY " A= '" || A"'"
PARSE VAR A . A
END
-------------------------------------------------------
If TIME() = '13:39:39'
After: 'PARSE VALUE TIME() WITH hh ":" mm ":" ss'
hh = '13'
mm = '39'
ss = '39'
A = "one two three four five six seven"
If you keep executing: 'PARSE VAR A . A'
THEN...
A= 'one two three four five six seven'
A= ' two three four five six seven'
A= ' three four five six seven'
A= ' four five six seven'
A= ' five six seven'
A= ' six seven'
A= ' seven'
PARSE Examples
Here are a couple of other examples of PARSE. In the first we are execution the TIME function and splitting the output, all in one instruction. In the second example we are breaking down a string word by word. We do this be coding the input string as one of the outputs. We are just take off the first word on each execution as you can see.
PARSE UPPER VAR InVar OutVar
where: UPPER = converts all chars to upper case.
PARSE UPPER PULL
where: UPPER = converts all chars to upper case.
PULL = get line from input stream
PARSE UPPER ARG
where: UPPER = converts all chars to upper case.
ARG = gets arguments going into program or function
UPPER, PULL & ARG
We haven't seem these before. UPPER can be coded after the word PARSE to ensure that we also do a case conversion. Simple and useful. In the second template here we have dropped the word 'OutVar', now we are getting our input from a different place. In the first line it is coming from the input stream. Exactly what that is will depend on where you are running your REXX. In second line ARG has been coded, here the input is coming from arguments, either passed to a function if it's coded in a function, or arguments passed to the program if its not coded in a function.
Now here something interesting, coding PULL actually gives you a PARSE UPPER PULL and coding ARG you get an PARSE UPPER ARG!