![]() |
The NetRexx Tutorial
![]() |
As we already said, NetRexx, like its cousin Java, is an object-oriented (OOP) language . The term object-oriented has become so widely used that we need to give it a more concrete meaning.
This section assumes no knowledge of object-oriented concepts.
At the end of this section, I hope that you'll get the feeling of how OOP can be "fun".
The Object Oriented Programming basic ideas are simple ones. Unfortunately, OOP has developed some special terminology, and many introductory works become totally incomprehensible to people encountering the subject food the first time.
OOP has four key concepts. You can remember them from the acronym "A PIE": think about the big pie that software vendors are sharing in selling us their OOP products. The components are:
A - Abstraction P - Polymorphism I - Inheritance E - Encapsulation
In the following part of the chapter, we will consider, as an example, the OOP representation of a 3 dimensional vector.
A 3d vector, we will see, can be defined in a computer using three numbers (this is the ABSTRACTION). A whole series of operations can be performed on a 3d vector (like inverting it, summing with other vectors, etc), making sure that we never corrupt the values of it (this is the ENCAPSULATION part). Using the concepts we used to define the 3d vector, we can build a 4d vector, keeping some of the functions we used to encapsulate the 3d vector (and this is the INHERITANCE part). Indeed, some functions (like the sum) must be overridden by the new 4d vector functions (to take account of the 4th dimension), and that' all for the POLYMORPHISM.
Resuming it in few lines definitely looks hard, but (you'll see) there is nothing more.
In this section we develop a simple example class, that we will call vector3d, that , as you can easily guess, will represent a geometric object in a three-dimensional space.
A vector, quoting Feynman, is three numbers. In order to represent step in space, say from the origin to some particular point P whose location is (x, y, z), we really need three numbers, but we are going to invent a single mathematical symbol, r. (...) It is not a single number, it represents three numbers: x, y and z. (FEYNMAN, 1963).
In those words Feynman has, de facto, extracted out the essential characteristics that we need to consider in order to represent a vector on a computer. This process is called abstraction.
Translating the above words in the NetRexx language, we get:
class vector3d public properties public xc -- x component yc -- y component zc -- z component
The important thing to note is that we did not define a real vector r. We just defined how we define a vector, i.e. with 3 quantities xc, yc, and zc.
The lines above contain two new keywords: class and properties.
The class keyword must be followed by the name of the class that we are defining. NOTE: this name MUST be the filename of the file we are writing: i.e. vector3d.nrx.
After the properties keyword we define the so called data-members, which are, de-facto, variable names.
There is a number of things that we can do with vectors: we can compute their magnitude (or module), we can inverse them. We can also execute operations with two vectors, like adding two vectors, computing their scalar product, check if they are equal, etc.
For each of those operations we then define a method which is, if you like, a sort of function that belongs to a class and that we perform over an object (belonging to this class).
-- method x() -- will return the x component of the 3d vector method x() public -- the code will go here -- method inverse() -- will inverse a 3d vector method inverse() public -- the actual code will go here -- method mag() -- will return the magnitude of a vector method mag() public -- the actual code will go here -- etc. etc.
Why we use the term method, and not just function or procedure ? The reason is just historical [VAN DER LINDEN, 1997] and goes back to Smalltalk-72. For you, just remember that a method is just a function that belongs to a class.
With the definition of the methods, we then have completed the class definition.
Resuming, if we want to capture the class of 3d vectors, (at least partially) in NetRexx code, we will write:
class vector3d public properties public xc -- x component yc -- y component zc -- z component method inverse() public xc = -xc yc = -yc zc = -zc method mag() public mag = Math.sqrt(xc*xc + xy*xy + xz*xz) return mag
When you define a class, you need to specify:
+----------------------------------------+ | | | CLASS | | | | +----------------------------+ | | | PROPERTIES | | | | (storage definitions) | | | | | | | +----------------------------+ | | | METHODS | | | | (operations on the | | | | PROPERTIES) | | | | | | | +----------------------------+ | | | +----------------------------------------+
The Objects are instances of a Class. So far we have defined how and what we can do to define and use a vector, but we need a "real" one, to try out the class definitions, and use it. We need an instance of the class.
By defining the vector3d class in NetRexx, we have now created a new data type. To have a REAL vector3d you then write:
v = vector3d()
Here you just told NetRexx: "please, treat the variable v as a vector3d: as I told you in the class definition, this variable will have 3 components associated to it, and I will be capable to perform operations like inverse , mag() etc. to it".
As you probably realize, all this procedure made you create "something" that is NOT a string. Infact, as I said, NetRexx has ONLY one NATIVE data type (the string) but you can create your own data types, and vector3d is just one example.
NOTE for Java Programmers: Note that this definition is a bit different of what you would do in Java. If you had to write the very same code in Java you would do:
vector3d v; v = new vector3d();
In NetRexx, the dynamical definition of the object is done automatically for you (saving one line of typing).
Now that we have a real vector3d object v, we can use its data fields and initialise it to some values.
We do it like this:
-- v is a vector3d object v = vector3d() -- initialise the vector 2 , 3 , 1 v.xc = 2 v.yc = 3 v.zc = 1
Consider the following definitions, were we define two vectors v1 and v2:
v1 = vector3d( 1 , 3 , 0) v2 = vector3d( 0 , 1 , 1)
It is important to consider how NetRexx defines those objects (and the class methods) in your computer's memory.
Objects: Code: +------+ +-------------------+ |xc=1 | | vector3d() | v1:|yc=3 |========>===========>| x() | |xc=0 | = | y() | | | = | z() | +------+ = | mag() | = | phi() | +------+ = | | |xc=0 | = | | v2:|yc=1 |========>= +-------------------+ |xc=1 | | | +------+
We can see that an object is an instance of a class (which is a new, user defined, type).
Each object (the vectors v1 and v2 in our example ) has its own data.
On the contrary, only ONE copy of the code for a class is shared by all the objects (that we now know we can call instances of the class).
So far, we just defined the vector v, but we have done nothing with it.
To access vector3d methods, we use the very same syntax we used to access the data of the object.
v = vector3d() -- Initialise values (...) m = v.mag() -- compute vector' mag p = v.phi() -- compute vector's PHI
In classical non-OO languages (FORTRAN, REXX, Pascal, etc.) the above call would have been written like:
m = mag(v) p = phi(v)
while, in NetRexx, we wrote:
m = v.mag() p = v.phi()
The difference is not just cosmetics: we are stressing the fact that the "center" of our attention is the v object, not the action that we are performing (the computation of the MAG or of PHI).
We see that properties and methods are considered at the same logical level (even if, as memory is concerned, treated in different ways).
So:
v.xc = 2 -- means: -- assign 2 to the xc component of v m = v.mag() -- means: -- apply method "mag()" to v, and store -- the result in "m"
If we look closer to the instruction we used to create a vector:
v = vector3d()
we notice the usage of the parentheses () after the vector3d. This looks really like a method call. Infact, we are calling a special method, called constructor, which is used to perform all the initialisations that are needed to prepare the new object.
The constructor is a "special" method, that's why it MUST have the same name of the class. So, since our class is called vector3d, to define the vector3d constructor method we'll write:
class vector3d ======== <-----------------+ | | -- constructor | method vector3d( ... ) public | ======== <-----------------+ | | | +--------------------- ------------+ |I MUST use the same name for | |the CLASS and for the CONSTRUCTOR | +----------------------------------+
Our first constructor will then look like:
-- method......: vector3d -- purpose.....: constructor -- method vector3d(x=Rexx,y=Rexx,z=Rexx) public this.xc = x this.yc = y this.zc = z
In order to use the constructor for our vector initialization, we'll then write:
v = vector3d(2,3,1)
which is exactly the same as writing, when we had not defined the constructor:
v = vector3d() -- ditto like v.xc = 2 -- v.yc = 3 -- v = vector3d(2,3,1) v.zc = 1 --
You'll find that having just one constructor method is usually not enough. Even in our simple class, it would be nice if it was possible to write something like:
zero = vector3d() -- define a vector 0 0 0 v = vector3d(3,2,1) z = vector3d(v) -- define a vector like v, -- i.e. 3 2 1 unary = vector3d(1) -- define a vector 1 1 1
You can do this in NetRexx writing "additional" methods, with the same name, but with different arguments. In our example, we'll write:
-- overloaded constructors -- method vector3d() public this(0,0,0) method vector3d(x=Rexx) public this(x,x,x) method vector3d(v1=vector3d) public this(v1.x,v1.y,v1.z)
What we just achieved is an operation of "method overloading", i.e. define a method with the same name, but different arguments.
So far we have defined 4 constructor methods, which are (just to summarise):
method vector3d(x=Rexx,y=Rexx,z=Rexx) public method vector3d() public method vector3d(x=Rexx) public method vector3d(v1=vector3d) public
This tells NetRexx that there are 4 ways to define a new vector. What happens if you try to write:
a = vector3d(1,2)
Simple: NetRexx does not know how to treat this case, so you'll get a very nasty message saying:
4 +++ a = vector3d(1,2) +++ ^^^^^^^^ +++ Error: cannot find constructor 'vector3d.vector3d(byte,byte)'
which means: "I do not know how to deal with this special case of vector3d followed by 2 arguments."
The main() method is a special one. It is the method that will automatically be called if you invoke a class directly from the command line.
Recall the parrot program:
+----------------------------------------------------------------------+ | /* parrot.nrx |01 | * echoes back what you type on command line |02 | */ |03 | parse arg s1 |04 | say 'you said "'s1'".' |05 | exit 0 |06 +----------------------------------------------------------------------+ parrot.nrx | ![]() |
If you want to write the very same code using a class, you'll do:
+----------------------------------------------------------------------+ | -- This class implements a class version of parrot.nrx |01 | -- |02 | class parrotc public |03 | |04 | method main(arguments=String[]) public static |05 | parse Rexx(arguments) s1 |06 | say 'you said "'s1'".' |07 | exit 0 |08 +----------------------------------------------------------------------+ parrotc.nrx | ![]() |
The two programs are perfectly equivalent (although the first one is definitely less typing). Infact, what NetRexx does is to translate the 1st one into "something" that looks like the 2nd one.
The main() method is very useful if you want to test a class. You will just put the class test cases, and run it typing java PROGNAME.
This is probably the most important section we've seen so far, since we finally apply in reality what we've been doing till now.
We have a file, called vector3d.nrx, that contains all the properties and methods used by the vector3d class. We compile it, and obtain a vector3d.class class file.
We can now edit a file that exercises the 3d vectors. The easiest one can be something like:
+----------------------------------------------------------------------+ | -- tvec3ds.nrx |01 | |02 | a = vector3d(1,1,1) -- define a vector |03 | say 'Vector "a" components:' a.components()'.' |04 | |05 | a.inverse() -- inverse it |06 | say 'Vector "a.inverse()" is' a.components()'.' |07 | exit 0 |08 | |09 +----------------------------------------------------------------------+ tvec3ds.nrx | ![]() |
As you can see, we do very little: just define a vector3d a, display his components, invert it, and check that all was OK.
We compile tvec3ds.nrx. NetRexx will grab the vector3d class definition at compile time, so it will know how a vector3d looks like. We end up with with a tvec3ds.class, which we can run as usual.
Resuming:
-- compile [1]> java COM.ibm.netrexx.process.NetRexxC vector3d.nrx [2]> java COM.ibm.netrexx.process.NetRexxC tvec3ds.nrx -- run [3]> java tvec3ds
To visually resume what we did, here's a picture:
+---------+ +---------+ | | | | |SOURCE | |SOURCE | | | | | +---------+ +---------+ vector3d.nrx tvec3ds.nrx = = = = (java IBM... vector3d) (java IBM... tvec3ds) = [1] = [2] = = +---------+ +---------+ | | | | |JAVA CODE|=============|JAVA CODE| | | | | +---------+ +---------+ vector3d.class tvec3ds.class = = = (java tvec3ds) = [3]
The following program illustrates all what we've implemented in the vector3d class.
+----------------------------------------------------------------------+ | -- tvec3d.nrx |01 | -- exercise the 3dim vector class |02 | -- |03 | a = vector3d(1) |04 | b = vector3d(3,4,3) |05 | c = vector3d() |06 | d = vector3d(b) |07 | e = vector3d() |08 | f = vector3d() |09 | |10 | say 'Vector "a" components:' a.components()'.' |11 | say 'Vector "b" components:' b.components()'.' |12 | say 'Vector "c" components:' c.components()'.' |13 | say 'Vector "d" components:' d.components()'.' |14 | say 'Say "a.mag()" is: 'a.mag()'.' |15 | |16 | e.zero() |17 | e.add(a) |18 | e.add(b) |19 | say 'Vector "a+b" is' e.components()'.' |20 | e.inverse() |21 | say 'Vector "e.inverse()" is' e.components()'.' |22 | |23 | e = vector3d.add(a,b) |24 | say 'Vector "a+b" is' e.components()'.' |25 | |26 | f = vector3d.greater(a,b) |27 | say 'Vector "greater(a,b)" is' f.components()'.' |28 | |29 | -- let's play with an array of vectors |30 | -- |31 | k = 200 |32 | v = vector3d[k] |33 | v[1] = vector3d(1,1,1) |34 | v[2] = vector3d(2,2,1) |35 | v[3] = vector3d(0,2,0) |36 | e.zero() |37 | loop i = 1 to 3 |38 | say 'vector "v['i']" is' v[i].components()'.' |39 | e.add(v[i]) |40 | end |41 | say 'Vector "INTEGRAL" is' e.components()'.' |42 | |43 | exit 0 |44 +----------------------------------------------------------------------+ tvec3d.nrx | ![]() |
*** This section is:*** and will be available in next releases
The vector3d class we defined is very good for classical physics. But, for relativistic studies, we need also to add another dimension: t.
This means that we need a new class, which we'll call vectorLo (as an abbreviation for vectorLorentz: a vector in the 4 dimension space).
NetRexx allows you to use the code we already wrote for the 3 dimension vector class, defining vectorLo as an extension (or subclass) of vector3d
We do this as:
class vectorLo public extends vector3d properties public (...) method (...)
The extends keyword tells NetRexx that the newly created vectorLo class is a subclass of vector3d. As such it INHERITS the variables and methods declared as public in that class.
That's where the real point is: we do not have to define again the method x(), in order to get the x component of a Lorentz vector, we just use the method we inherited from the 3 dimensional vector3d class.
Some methods, of course, need to be overloaded, like in the case of:
-- method......: components -- purpose.....: prints the components -- method components() public returns string return '('this.xc','this.yc','this.zc','this.tc')'
to take into account the new dimension.
The Lorenz's vector implementation will be:
+----------------------------------------------------------------------+ | -- This class implements a Vector in a 4 dimentional space |01 | -- |02 | class vectorLo public extends vector3d |03 | properties public |04 | tc |05 | |06 | -- method......: vectorLo |07 | -- purpose.....: constructor |08 | -- |09 | method vectorLo(x=Rexx,y=Rexx,z=Rexx,t=Rexx) public |10 | super(x,y,z) |11 | this.tc = t |12 | |13 | method vectorLo() public |14 | this(0,0,0,0) |15 | |16 | method vectorLo(x=Rexx) public |17 | this(x,x,x,x) |18 | |19 | method vectorLo(v1=vectorLo) public |20 | this(v1.xc,v1.yc,v1.zc,v1.tc) |21 | |22 | -- method......: components |23 | -- purpose.....: prints the components |24 | -- |25 | method components() public returns string |26 | return '('this.xc','this.yc','this.zc','tc')' |27 | |28 | -- method......: main |29 | -- purpose.....: runs the test case |30 | -- |31 | method main(args=String[]) public static |32 | args=args |33 | a = vectorLo(1,1,1,1) |34 | b = a |35 | |36 | say 'Vector "a" components:' a.components()'.' |37 | say 'Vector "b" components:' b.components()'.' |38 | say b.mag() |39 | |40 | exit 0 |41 +----------------------------------------------------------------------+ vectorLo.nrx | ![]() |
Just to clear out the terminology we speak about superclasses and sublasses, saying:
+---------------+ | vector3d | +---------------+ +-(...is a superclass of...)<--+ | | | | | | +->(...is a subclass of...)----+ +---------------+ | vectorLo | +---------------+
or, if you prefer:
+--------+ |KEYWORD | +--------+ | V class vectorLo public extends vector3d | | | | V V +---------+ +-----------+ |SUBCLASS | |SUPERCLASS | |INHERITS | | | +---------+ +-----------+
It is sometimes useful to check if we have a particular subclass, within a superclass, and perform this check at runtime.
Java programmers might use the instanceof operator; in NetRexx you just do:
object <= class_name
using the <= operator.
So, for example, we might have:
class vector3d public (...) class vectorLo public extends vector3d (...) class vectorHEP public extends vectorLo (...) v1 = vectorLo() if v1 <= vectorLo
Another way to test for a class match, as suggested by Mike Cowlishaw in a recent thread is:
if OBJECT.getclass.getname == 'CLASS' then
I resume the above discussion in the following code:
+----------------------------------------------------------------------+ | -- tvecLo1.nrx |01 | -- |02 | a = vectorLo(1,2,1,1) -- a Lorentz vector |03 | b = vector3d(1,1,1) -- a 3d vector |04 | |05 | -- check if a and b are Lorentz vectors |06 | -- |07 | if a <= vectorLo |08 | then say 'a is a Lor vec' |09 | if b <= vectorLo |10 | then say 'b is a Lor vec' |11 | |12 | -- get in another way |13 | -- |14 | say 'a is a:' a.getclass.getname |15 | say 'b is a:' b.getclass.getname |16 | |17 | exit 0 |18 +----------------------------------------------------------------------+ tvecLo1.nrx | ![]() |
I presented the example of the 3 dimensional and 4 dimensional vector classes mainly for "educational" purposes. We saw infact a "minor" problem which is the need to write again some methods for the 4 dimensional vector class, because we need to take into account the fact that we have an extra dimension (remember the mag() method). So, if we have to deal with 5 dimensional vectors, we'll need to rewrite AGAIN those methods.
There MUST be a better approach; the idea is to write a class which has NO notion of the space dimension, and use that to build the 3d, 4d, 5d, etc. vectors.
This class will be called xvector.nrx.
The xvector class will implement a N-dimensional vector. We are then forced to use arrays to hold the numerical values.
The mag() method will look like:
+----------------------------------------------------------------------+ | -- method......: mag |97 | -- purpose.....: vector's elements mag |98 | -- |99 | method mag() public |00 | sum = 0 |01 | loop i = 1 to this.dimension |02 | sum = sum + this.element[i]*this.element[i] |03 | end |04 | sum = Math.sqrt(sum) |05 | return sum |06 | |07 +----------------------------------------------------------------------+ xvector.nrx(Method:mag) | ![]() |
+----------------------------------------------------------------------+ | -- method......: add |24 | -- purpose.....: adds a vector to another |25 | -- |26 | method add(v1=xvector,v2=xvector) public static returns xvector |27 | v3 = xvector('0') |28 | v3.dimension = v1.dimension |29 | loop i = 1 to v1.dimension |30 | v3.element[i] = v1.element[i] + v2.element[i] |31 | end |32 | return v3 |33 | |34 +----------------------------------------------------------------------+ xvector.nrx(Method:add) | ![]() |
Look now how simple is to build our 3 dimension vector class: we just extend the xvector class and override the constructor, to allow writing:
v = xvector3d(1,2,3)
I'll call the new 3d vector class xvector3d, to avoid confusion with the vector3d one we studied in the previous sections.
+----------------------------------------------------------------------+ | -- This class implements a Vector in a 3 dimensional space |01 | -- extending the xvector class |02 | -- |03 | class xvector3d public extends xvector |04 | |05 | -- method......: vectorLo |06 | -- purpose.....: constructor |07 | -- |08 | method xvector3d(x=Rexx,y=Rexx,z=Rexx) public |09 | super(x','y','z) |10 | |11 | method xvector3d() public |12 | this('0','0','0') |13 | |14 | method xvector3d(x=Rexx) public |15 | this(x,x,x) |16 | |17 | method xvector3d(v1=xvector3d) public |18 | this(v1.element[1],v1.element[2],v1.element[3]) |19 | |20 | -- method......: main |21 | -- purpose.....: runs the test case |22 | -- |23 | method main(args=String[]) public static |24 | args=args |25 | a = xvector3d(1,1,1) |26 | b = a |27 | |28 | say 'Vector "a" components:' a.display()'.' |29 | say a.mag() |30 | say 'Vector "b" components:' b.display()'.' |31 | |32 | exit 0 |33 +----------------------------------------------------------------------+ xvector3d.nrx | ![]() |
After having dealt with vectors, which might not be interesting for you, if you're not a physicist or an engineer, let's start with some real objects that you are dealing with everyday.
The command line is one of those objects. With command line I mean all what you enter after a program's name on the command line (shell or DOS prompt).
prompt> java my_command arguments -options ------------------ | +--------------+ | COMMAND LINE | +--------------+
A command line is usually divided in
Just take a UNIX book and you'll find hundreds, if not thousands of examples. I give you a really small sample:
command options arguments ---------------- ------- --------- ls -la test tost -l -a test tost df -k /usr -k /usr cat test NONE test tar -cvf o.tar * -c -v * -f tar
The operations that we do, when analysing a command line in a in a program are (in random order):
Since we want to be clever, we add also some requirements:
We want that the arguments and options can be intermixed: this means that:
myprog -t -o test.file input_arg myprog -to test.file input_arg myprog input_arg -o test.file -t
MUST be perfectly equivalent from the user's point of view. (note that this is not always true in UNIX!).
Also, we want to be capable to query, at any time in the program, the value of an option, in order to write something like:
il = cmdline() (...) if il.option('TRACE') then say 'Tracing is active' (...)
In the actual implementation, we need indeed an additional information, which is "how to pass the options and their default value when we create the cmdline?".
A way is to use a string that holds, separated by a delimiter, the value of :
- the symbol of the option (like r, t, o, etc.); - a parameter indicating if it's a flag or a variable; - the NAME of the option - the default value
We will call this string the rules definition, since we use those rules to define the options.
Example:
't/FLA/TRACE/0'
we define an option (-t) which is a flag, known in our program as 'TRACE' and defaulted to 0
'o/VAR/OUTFID/test.output'
we define an option (-o) which defines a variable, known in our program as 'OUTFID' and defaulted to test.output
't/FLA/TRACE/0 o/VAR/OUTFID/test.output'
our rules definition is now to have two options, the same as above
The cmdline constructor will accept two arguments: the first one being a rexx string containing the line entered by the user; the second one being again a rexx string, containing the rules in the format we defined. This allows us to already prepare all the options and all the arguments.
+----------------------+ |USER'S INPUT | |like: file1 -t -o test| +----------------------+ | cl = cmdline( inputline , rules ) | | | +------------------------------+ | | PROGRAMMER'S RULES | | | like: 't/FLA/TRACE/0' - | | | 'o/VAR/OFID/test.out' | | +------------------------------+ | +------------------------------------+ | This object is now aware of the | | options as entered by the user | | allowing something like: | | if \cl.option('TRACE') then ... | +------------------------------------+ |
The class will look like:
class cmdline properties private options argument (...) method cmdline(rexx,rexx) public method option(rexx) public method verify(rexx) public method optiondump() public (...)
I show now how some of the class methods are implemented.
By far the most complex is the cmdline constructor. We need infact to analyse the command line, as entered by the user (instr) and parse the options as defined by the programmer (rules).
The first step is to check the rules, set the valid options and set the default option values.
+----------------------------------------------------------------------+ | -- method......: cmdline |95 | -- purpose.....: constructor |96 | -- |97 | method cmdline(instr=Rexx,rules=Rexx) public |98 | |99 | -- initial setup |00 | -- |01 | olist = '' -- option_list |02 | oinfo = '' -- option info |03 | outstr = '' -- that's the string that holds all BUT the |04 | -- options; we'll return this |05 | |06 | -- set the defaults |07 | -- |08 | loop for rules.words() |09 | parse rules rule rules |10 | parse rule opt'/'info |11 | olist = olist opt |12 | oinfo[opt] = info |13 | parse info kin'/'nam'/'def |14 | select |15 | when kin = 'FLA' then |16 | do |17 | value[nam] = def |18 | end |19 | when kin = 'VAR' then |20 | do |21 | def = def.translate(' ','$') |22 | value[nam] = def |23 | end |24 | otherwise |25 | do |26 | say '(parse_UXO) Internal error.' |27 | say '(parse_UXO) kin was "'kin'".' |28 | say '(parse_UXO) Aborted.' |29 | exit 901 |30 | end |31 | end |32 | end |33 | |34 | -- get the options as entered |35 | -- |36 | loop while instr <> '' |37 | parse instr var instr |38 | if var.left(1,1) <> '-' then |39 | do |40 | outstr = outstr var |41 | Iterate |42 | end |43 | svar = var |44 | var = var.substr(2,1) |45 | if olist.wordpos(var) = 0 then |46 | do |47 | say 'Invalid option "'var'" selected.' |48 | say 'Valid options are "'olist.space()'".' |49 | say 'Program aborted.' |50 | exit 902 |51 | end |52 | info = oinfo[var] |53 | parse info kin'/'nam'/'def |54 | select |55 | when kin = 'FLA' then |56 | do |57 | if def = '0' |58 | then def = '1' |59 | else def = '0' |60 | value[nam] = def |61 | end |62 | when kin = 'VAR' then |63 | do |64 | def = def.translate(' ','$') |65 | cho = '' |66 | loop for def.words() |67 | parse instr tt instr |68 | if tt = '' then |69 | do |70 | say 'Invalid argument for option "'var'".' |71 | say 'Should be a' def.words() 'words string.' |72 | say 'Like default "'def'".' |73 | say 'Program Aborted.' |74 | exit 903 |75 | end |76 | cho = cho tt |77 | end |78 | value[nam] = cho.space() |79 | end |80 | otherwise NOP |81 | end |82 | -- here I deal with the case when one enters |83 | -- -tf instead of -t -f |84 | -- |85 | if svar.length() <> 2 then |86 | do |87 | ll = svar.length() - 2 |88 | oo = svar.substr(3,ll) |89 | instr = '-'oo instr |90 | end |91 | end |92 | argumentlist = outstr.space() |93 | |94 +----------------------------------------------------------------------+ xstring.nrx(Method:cmdline) | ![]() |
+----------------------------------------------------------------------+ | -- method......: option |95 | -- purpose.....: |96 | -- |97 | method option(in=Rexx) public |98 | out = value[in] |99 | return out |00 | |01 +----------------------------------------------------------------------+ xstring.nrx(Method:option) | ![]() |
This two additional examples should clarify what we did.
+----------------------------------------------------------------------+ | -- test for the cmdline class |01 | -- |02 | -- we allow 2 options: |03 | -- -t (TRACE) flag default to 0 |04 | -- -o (OUTFID) variable defaulted to test.out |05 | -- |06 | parse arg argsl |07 | |08 | cl = cmdline(argsl,'t/FLA/TRACE/0' - |09 | 'o/VAR/OUTFID/test.out') |10 | say 'The arguments are:' cl.arguments()'.' |11 | if cl.option('TRACE') |12 | then say 'Tracing is ON' |13 | else say 'Tracing is OFF' |14 | say 'The output file is:' cl.option('OUTFID') |15 | |16 | exit 0 |17 +----------------------------------------------------------------------+ tcl1.nrx | ![]() |
+----------------------------------------------------------------------+ | -- another test |01 | -- |02 | class tcl2 |03 | properties public |04 | |05 | method tcl2() public |06 | |07 | method main(ar=String[]) public static |08 | argsl = xstring.a2s(ar) |09 | |10 | -- test for the cmdline class |11 | -- |12 | -- we allow 2 options: |13 | -- -r (REPLACE) flag default to 0 |14 | -- -T (TESTLEVEL) variable defaulted to 0 |15 | -- |16 | cl = cmdline(argsl,'r/FLA/REPLACE/0' - |17 | 'T/VAR/TESTLEVEL/0') |18 | say 'The arguments are:' cl.arguments()'.' |19 | if cl.option('REPLACE') |20 | then say 'Replace is ON' |21 | else say 'Replace is OFF' |22 | say 'The testlevel is:' cl.option('TESTLEVEL') |23 | |24 | exit 0 |25 +----------------------------------------------------------------------+ tcl2.nrx | ![]() |
As we have seen, the arguments in the main() method are passed as an array of string[].
This is clearly different from the approach we saw in Chapter 2 about the argument passing from the command line, where arg was returning a simple NetRexx string.
To get the arguments in the "right" way (i.e. the way you have been used to) you need to code an extra line:
method main(args=String[]) public static arg = Rexx(args) -- ADD THIS LINE parse arg p1 p2 . -- THIS as usual --
The line:
arg = Rexx(args)
instruct NetRexx to "translate" the array of string args into a single NetRexx variable string.
args[0] -+---( Rexx() )--> arg args[1] -+ (...) args[n] -+
*** This section is:*** and will be available in next releases
*** This section is:*** and will be available in next releases