![]() |
The NetRexx Tutorial
![]() |
No algorithmic language would be complete without instructions that allow the execution of statements depending on certain conditions for performing iterations and selections. NetRexx has many such instructions for allowing program flow control. Probably the most important is the do...end construct.
A statement block is a sequence of statements enclosed by a do (...) end. A statement block looks like this:
do statement_1 statement_2 (...) statement_N end
NetRexx executes these statements in sequence Ñ from the first to the last. Syntactically, a block of statements is accepted in place of any single statement.
The if/then/else construct is used to conditionally execute an instruction or a group of instructions. The if/then/else construct can also be used to select between two alternatives.
if expression then instruction else instruction
The expression is evaluated, and MUST result in '0' or '1'. Thus, as you can imagine:
if expression then instruction if expression results to 1 else instruction if expression results to 0
It is usually difficult to do 'nothing'. However, the nop instruction was created for just such a purpose: it is a dummy instruction.
NOP
It is useful as target for a then or else clause:
-------------------------------------------------------- if a = 3 then NOP else say 'a is NOT 3.' -------------------------------------------------------- example of NOP
The loop instruction is used (as we have already seen), to group a set of instructions, and to execute (optionally) more than once. In its easier case, the loop for looks suspiciously like the C-language for statement. Let us consider a first case:
loop for expression statement_1 statement_2 (...) statement_N end
In this case, expression - an expression that evaluates a number - tells NetRexx 'how many times to execute the loop'. Here is an example:
-------------------------------------------------------- /* this statement will be executed 3 times */ loop for 3 say 'Hello' end -------------------------------------------------------- do N example
Will print on your screen:
Hello Hello Hello
-------------------------------------------------------- list = 'MARTIN JOE PAULA' /* this statement will be executed 3 times */ loop for list.words() parse list name list say 'Hi!' name end -------------------------------------------------------- second do N example
Will print on your screen:
Hi MARTIN Hi JOE Hi PAULA
Of course, you can use a variable (which we will regard as an index) to run the iteration. This is a 'controlled repetitive loop'. A more complex case is the following:
loop name = expr1 to expr2 statement_1 statement_2 (...) statement_N end
Examples:
-------------------------------------------------------- loop i = 1 to 5 say i end -------------------------------------------------------- loop example
Will print on your screen:
1 2 3 4 5
-------------------------------------------------------- cols = 2 rows = 3 loop i = 1 to cols loop j = 1 to rows say j end end -------------------------------------------------------- loop with 2 indices
Will print on your screen:
1 2 3 1 2 3
In the above examples, we always incremented by a positive quantity (+1). What about when your increment is NOT +1? The solution is again a do, but now with a by statement. Our do loop will then look like:
loop varname = expr1 to expr2 by expr3 statement_1 statement_2 (...) statement_N end
And here are some examples:
-------------------------------------------------------- loop i = 2 to -1 by -1 say i end -------------------------------------------------------- by example
Will print on your screen:
2 1 0 -1
-------------------------------------------------------- x1 = 2.1 x2 = 2.5 increment = .1 loop x = x1 to x2 by increment say x end -------------------------------------------------------- by example
Will print on your screen:
2.1 2.2 2.3 2.4 2.5
You can even add a repetition counter, which sets a limit to the number of iterations if the loop is not terminated by other conditions. Our loop loop will then look like the following:
loop varname = expr1 to expr2 by expr3 for expr4 statement_1 statement_2 (...) statement_N end
Example:
-------------------------------------------------------- y_start = .9 y_end = 2.7 loop y = y_start to y_end by .9 for 2 say y end -------------------------------------------------------- for example
Will print on your screen:
.9 1.8
The while and until constructs commonly found in other programming languages are also available in NetRexx, as a condition to the ubiquitous loop statement. Here is how to build a simple while loop:
loop while expression statement_1 statement_2 (...) statement_N end
And here is how to build a simple until loop:
loop until expression statement_1 statement_2 (...) statement_N end
Consider the example:
-------------------------------------------------------- i = 1 loop while i < 7 say i '\-' i = i+1 end -------------------------------------------------------- while example
---> The previous code will print: 1 2 3 4 5 6
-------------------------------------------------------- i = 1 loop until i > 6 say i '\-' i = i+1 end -------------------------------------------------------- until
---> Will print: 1 2 3 4 5 6
A nice NetRexx feature is that you can combine the loop in its repetitive form with the loop in its conditional form (i.e. the while/until construct we just considered). This can lead to constructs that look like:
-------------------------------------------------------- loop i = 1 to 10 while i < 6 say i '\-' end -------------------------------------------------------- combined example
---> This code will print: 1 2 3 4 5. There is a nice 'side effect' to this feature, and that is the possibility of building a while/until loop without incrementing (or decrementing) the control variable yourself. Consider the case we just looked at:
-------------------------------------------------------- i = 1.0 loop while i < 3 say i '\-' i = i+.5 end -------------------------------------------------------- do while example
---> This code will produce: 1.0 1.5 2.0 2.5 We need to define the start value i = 1.0, and define the step increment i = i+.5. All this can be avoided with the following construct:
-------------------------------------------------------- loop i = 1.0 by .5 while i < 3 say i '\-' end -------------------------------------------------------- do by while example
---> Will print: 1.0 1.5 2.0 2.5 This code is much more compact. A resume' of what we have seen so far on the do instruction:
---------------------------------------------------------------------- loop repetitor conditional --------- ----------- | | | +----------< _ WHILE expr_w | _ UNTIL expr_u | +------------< _ var = expr_i TO expr_t BY expt_b FOR expr_f _ expr_r _ FOREVER instruction_1 instruction_2 (...) instruction_N end ----------------------------------------------------------------------
The select instruction is used to execute one of several alternative instructions. The format is:
select when expression_1 then instruction_1 when expression_2 then instruction_2 when expression_3 then instruction_3 (...) otherwise instruction_N end
What NetRexx does is evaluate the expressions after the when. If the result is '1', then what follows the corresponding then is executed (this can be anything Ñ a single instruction, a set of instructions inside a do ... end clause, etc.). Upon return, the control will pass directly to the end instruction. If none of the when expressions result in a '1', then the otherwise instruction is executed. NOTE: the otherwise clause is NOT mandatory, but if none of the when expressions results in a '1', and the otherwise is not present, you will get a 'SYNTAX error'. It is thus wise to ALWAYS add an otherwise clause at the end of a select, usually with a NOP instruction.
-------------------------------------------------------- (...) /* this will print a flag corresponding to the */ /* inactivity time of a terminal: */ /* the table is the following */ /* hour 0...1...2...3...4...5...6...7...8 */ /* flag ****;;;;::::::::................. */ /* where 'hour' is since how many hours the */ /* terminal is inactive, and flag is the */ /* flag we want to display */ /* inactive: time (in hours) a terminal */ /* has been inactive */ select when inactive < 1 then flag = '*' when inactive < 2 then flag = ';' when inactive < 4 then flag = ':' otherwise flag = '.' end (...) -------------------------------------------------------- select example
Use the iterate instruction to alter the flow of control within a repetitive do loop (i.e. any do construct which is NOT a plain do). The syntax is:
do (expression) statement_1 (...) statement_N (condition) iterate [name] statement_N+1 (...) statement_M end
If program flow reaches the iterate instruction, the control is passed back to the do instruction, so that the statements statement_N+1,...statement_M are NOT executed. Here is an example:
-------------------------------------------------------- loop i = 1 to 5 say '* \-' if i = 3 then iterate say i '\-' end -------------------------------------------------------- iterate example
---> This will print: * 1 * 2 * * 4 * 5 The iterate instruction also supports a 'name' following it, and name (if present) must be the variable name of a current active loop. Consider this following code atom:
-------------------------------------------------------- num = 7 loop i = 1 to num line = " loop j = 1 to num if i = j then do say line iterate i end line = line j end end -------------------------------------------------------- iterate example II
This code will print:
1 1 2 1 2 3 1 2 3 4 1 2 3 4 5 1 2 3 4 5 6
Use the leave instruction to exit immediately from a do loop. The syntax is:
loop (expression) statement_1 (...) statement_N (condition) leave [name] statement_N+1 (...) statement_M end
The flow of control is passed to the instruction that FOLLOWS the corresponding end in the loop loop. Here is an example:
-------------------------------------------------------- loop i = 1 to 5 say '* \-' if i = 3 then leave say i '\-' end --------------------------------------------------------
---> The above code will produce the output: * 1 * 2 * You should note that leave is similar, in a certain sense, to the iterate instruction: like it, leave 'breaks' the normal flow of control in the do loop. Pictorially:
loop <------+ (...) | (...) (back to beginning) (...) | iterate -------+ leave -------+ (...) | (...) (jump past the end) (...) | end | <------+
As usual, we now present some 'real-life' examples.
As you may have noticed, the foreach instruction does not exist in NetRexx. And if you are a shell programmer, you may well also be without it. However, here is a trick for simulating it with a minimum of effort:
-------------------------------------------------------- loop while list ^= " | -> foreach item (list) parse list item list | (...) | end | end -------------------------------------------------------- foreach example
The only thing you need to remember is that the list variable, at the end of the do loop, will be NULL; remember to save it if you plan to use it later.
Configuration files are usually divided in the UNIX terminology into 'stanzas'. A 'stanza' is a uniquely identified portion of the file that contains the parameters for a specified entity. VM programmers may identify a 'stanza' as a single entry in a NAMES file: an identifier marks the start of a stanza, and a set of parameters follows, until a new stanza (or an End_of_File) is reached. Let us look at a 'stanza' example:
+------------------------------------------------------------------+ | # comment line | | node: rsl3pm1 # first stanza | | machine: rs6000 # defines node | | vendor: IBM # rsl3pm1 | | location: b32r035 # | | | | node: sgl3pm1 # second one | | machine: Indigo2 # defines node | | vendor: SGI # sgl3pm1 | | location: b11r023 # | | | | node: hpl3sn05 | | machine: 730/50 | | vendor: H/P | | location: b71r233 | +------------------------------------------------------------------+ Source file: test.stanza |
You should note that:
The following program is composed of a small call to a routine that does the job of:
As you can see, the function is a good example of utilisation of the do, leave, iterate instructions.
+----------------------------------------------------------------------+ | -- readst.nrx |01 | -- |02 | parse arg nodeid . |03 | |04 | -- |05 | -- |06 | |07 | -- read the file |08 | -- |09 | infid = xFile('test.stanza') |10 | rc = infid.rd_file() |11 | if rc <> 0 then |12 | do |13 | say 'problem reading "'infid.name'".' |14 | exit 1 |15 | end |16 | |17 | output = '' |18 | found = 0 |19 | loop i = 1 to infid.line[0] |20 | if infid.line[i] = '' then iterate |21 | parse infid.line[i] key rest '#' . |22 | if key = '#' then iterate |23 | if key = 'node:' then |24 | do |25 | if found then leave |26 | if rest = nodeid then |27 | do |28 | found = 1 |29 | iterate |30 | end |31 | end |32 | if found = 0 then iterate |33 | parse infid.line[i] line '#' . |34 | output = output line |35 | end |36 | out = output.space() |37 | if out = '' |38 | then say 'Not found.' |39 | else say output.space() |40 | |41 | exit 0 |42 +----------------------------------------------------------------------+ readst.nrx | ![]() |
NOTEs:
Run this program and here is the result you will get:
.................................................................... rsl3pm1 (182) java readst sgl3pm1 machine: Indigo2 vendor: SGI location: b11r023 rsl3pm1 (183) java readst rsl3pm1 machine: rs6000 vendor: IBM location: b32r035 rsl3pm1 (184) .................................................................... readst.output |
The following problem might appear totally 'academic'. It did to me until I encountered the following problem. A directory contained a set of files (more than 20 000), each identified by a number (as filename). To make the problem clearer, my directory contained these files:
10000 10001 10002 10003 10004 10005 10006 10007 (...) 33002 33003 33004 33005
The user needed to perform operations on a subset of the files Ñ for example:
10000 10981 10982 10983 21900 21901 or: 30291 30292 or: 67234 67235 67236 67237 77889 88974 88975
The user had to start from N and continue until item M, or from item J for K files. There was no easy solution with UNIX standard wild-cards. And the only solution was to write the items one by one. The small program (and routine) that follows is a possible solution to the problem Ñ it expands a pattern according to a very simple syntax:
first-last first.how_many
The expansion is then of the type:
10020-10022 -> 10020 10021 10022 30452.4 -> 30452 30453 30454 30455
The program will accept any combination of items containing '.' or '-', or simple single items. The program is really very simple:
+------------------------------------------------------------------+ | parse arg teststr |01 | say expandlist(teststr) |02 | exit 0 |03 +------------------------------------------------------------------+ explist.nrx | ![]() |
And of course requires this small function: (I present it separately so that you can quickly put it inside a bigger program if you like it).
+----------------------------------------------------------------------+ | -- method......: listexpand |72 | -- purpose.....: |73 | -- |74 | method listexpand(il=Rexx) public static |75 | ol = '' |76 | loop while il <> '' |77 | parse il it il |78 | if it.pos('.') <> 0 then |79 | do |80 | parse it f'.'n |81 | loop i = f to f+n-1 |82 | if ol.pos(i) <> 0 then iterate i |83 | ol = ol i |84 | end |85 | iterate |86 | end |87 | if it.pos('-') <> 0 then |88 | do |89 | parse it f'-'l |90 | loop i = f to l |91 | if ol.pos(i) <> 0 then iterate i |92 | ol = ol i |93 | end |94 | iterate |95 | end |96 | if ol.pos(it) <> 0 then iterate |97 | ol = ol it |98 | end |99 | Return ol |00 | |01 +----------------------------------------------------------------------+ xstring.nrx(Method:listexpand) | ![]() |
Here is what you can use it for:
.................................................................... rsl3pm1 (9) explist 2000 3045.3 7002-7003 2000 3045 3046 3047 7002 7003 rsl3pm1 (11) echo `explist 20000 30890-30900` 20000 30890 30891 30892 30893 30894 30895 30896 30897 30898 30899 30900 rsl3pm1 (12) ls -la `explist 20000 30890-30900` (...) rsl3pm1 (13) cat `explist 20000.7 30890-30900` > toto (...) .................................................................... explist.out |
It is sometimes usefull to convert information from an array, to a string, and viceversa.
+----------------------------------------------------------------------+ | -- method......: a2s |31 | -- purpose.....: converts a Rexx array to a string |32 | -- |33 | method a2s(a=Rexx) public static |34 | a = a |35 | out = '' |36 | loop i = 1 to a[0] |37 | out = out a[i] |38 | end |39 | return out |40 | |41 +----------------------------------------------------------------------+ xstring.nrx(Method:a2s) | ![]() |
+----------------------------------------------------------------------+ | -- method......: s2a |18 | -- purpose.....: converts a string to an array |19 | -- |20 | method s2a(str=Rexx,a=Rexx) public static |21 | a = a |22 | i = 0 |23 | loop while str <> '' |24 | parse str nn str |25 | i = i+1 |26 | a[i] = nn |27 | end |28 | a[0] = i |29 | |30 +----------------------------------------------------------------------+ xstring.nrx(Method:s2a) | ![]() |
The following example will show the utilization of such functions.
+----------------------------------------------------------------------+ | -- simple test of a2s and s2a |01 | -- |02 | |03 | -- convert a string to an array |04 | -- |05 | b = rexx(") |06 | xstring.s2a('52 45 66 3 4',b) |07 | loop i = 1 to b[0] |08 | say i ':' b[i] |09 | end |10 | |11 | -- convert an array to a string |12 | -- |13 | c = rexx(") |14 | c[0] = 3 |15 | c[1] = 'This is a test' |16 | c[2] = 'another el.' |17 | c[3] = 'LAST ONE.' |18 | |19 | s = xstring.a2s(c) |20 | say s |21 | |22 | exit 0 |23 +----------------------------------------------------------------------+ tarray.nrx | ![]() |
*** This section is:*** and will be available in next releases
A resume' of some of the concepts we've encountered in this chapter:
_ block of instructions | do (...) end | - ex.: do | instructions | instructions | end | _ 'for' loop | loop for n=n1 to n2 (...) end | - ex.: loop i = 1 to 6 | instructions | instructions | end | _ 'while' loop | loop while expr (...) end | - ex.: loop while i < 6 | instructions | instructions | end |
*** This section is:*** and will be available in next releases