This section contains a list of some new features in Object-Oriented REXX that are useful even for Classic REXX programs.
Please do not forget to check the version of the REXX interpreter you're using with your REXX programs before you attempt to use on of these features!
---------- * ----------
DO i OVER stem
This is a very useful enhancement of DO loops. Now you can simply walk over all elements of a stem without knowing the tails:
/* drop all stem entries */ drop myStem. do i = 1 to 40 j = random( 400 ) myStem.j = 1 end /* do i = 1 to 40 */ do i over myStem. /* "i" contains the name of the next tail */ /* (to get the value use "myStem.i") */ say 'The ' || i || ' was at least one time set' say 'The ' || myStem.i || ' was at least one time set' end /* do i over myStem */
---------- * ----------
Returning a stem variable
In Object-Oriented REXX a routine can return a stem variable. Example:
/* */ test. = test1() do i = 1 to test.0 say test.i end return test1: /* init a local stem ... */ a.0 = 3 a.1 = 11 a.2 = 22 a.3 = 33 /* ... and return it to the calling routine. */ return a.
---------- * ----------
Calculations and other stem variables are now possible to get or set a stem variable. Example:
/* init a stem with sample values */ do i = 1 to 500; test.i = i * 2 end /* do */ test.0 = 500 /* access the stem in the Classic way */ say test.5 /* (1) */ j = 4+55 /* (2) */ say test.j i = test.4 /* (3) */ say test.i i = test.4 /* (4) */ j = test.i say test.j /* use the new way */ say test.[5] /* (1) */ say test.[4+55] /* (2) */ say test.[test.4] /* (3) */ /* (4) */ say test.[test.[test.4]]
---------- * ----------
PARSE [upper|lower|caseless]
The PARSE instruction now supports lower and caseless parsing.
---------- * ----------
Call-by-Reference-Parameters are now possible for internal and external REXX procedures (at least for stem variables):
/* */ say say 'Sample code to show the usage of USE ARG in Object REXX ' say j.0 = 2 j.1 = 111 j.2 = 222 say 'Values of the variables before calling the sub routine:' say say ' j.0 is ' || j.0 do k = 1 to j.0 say ' j.' || k || ' is ' || j.k end /* do */ say say 'Now calling TestUseArg ...' say call TestUseArg j. say say 'Values of the variables after calling the sub routine:' say say ' j.0 is ' || j.0 do k = 1 to j.0 say ' j.' || k || ' is ' || j.k end /* do */ exit TestUseArg: PROCEDURE use arg local_j. /* local_j points to the global stem j. */ local_j.0 = 3 local_j.1 = '111 - one' local_j.2 = '222 - two' local_j.3 = '333 - three' return
---------- * ----------
CALL now accepts variables that are evaluated before the CALL statement is executed:
/* */ myRoutine = 'MYTEST' call (myRoutine) exit MYTEST: say 'This is mytest!' RETURN
Plese note that the contents of the variable must be in uppercase if calling an internal routine. This is not clearly stated in the online help.
---------- * ----------
DATE is not restricted to the current date any more. Now you can use the results of this function for date calculations (e.g. How many days are between day 1 and day 2?).
---------- * ----------
STREAM now supports some more commands and options; for example FLUSH, REPLACE (rewrite a file without doing a DEL first), and NOBUFFER. It also supports line-related positioning for files with fixed and variable length records. And there are now two different file pointers for every file -- one for reading from it and one for writing to it.
---------- * ----------
TIME is not restricted to the current time any more. This allows calculations with time stamps.
---------- * ----------
Use the environment .local for variables global to the current process. Example:
/* create a variable global to the current process */ .local['BS.MYVAR'] = 'This is a global variable' /* call an external REXX routine to show that it works */ call rexxtry "say .local['BS.MYVAR'] " call rexxtry ".local['BS.MYVAR'] = 'This variable is set by REXXTRY'" say .local['BS.MYVAR'] /* you can also use the following code to read the variable */ /* But be aware of the search order for environment symbols in */ /* Object REXX! */ say .BS.MYVAR
Note: "To avoid conflicts with future REXX-defined entries, it is recommented that entries you place in the program local environment or in the global environment include a least one period in the entry name."
Use the environment .environment for variables global to all REXX programs. Example:
/* create a variable global to all REXX programs */ .environment['BS.MYVAR'] = 'This is a global variable' /* start a REXX program in another process to show that it works */ "cmd /c rexxtry say .environment['BS.MYVAR'] " "cmd /c rexxtry .environment['BS.MYVAR'] = 'This variable is set by REXXTRY'" say .environment['BS.MYVAR'] /* you can also use the following code to read or change the variable */ say value( 'BS.MYVAR',,'' ) call value 'BS.MYVAR', 'Value set using the VALUE function', ''
Note that the environment .environment contains a lot of default variables that you should NOT change. To get a list of all existing variables you can use the following code:
/* show all variables in the environment .environment */ do i over .environment say 'Variable "' || i || '" is "' || .environment[i] || '"' end /* do i over .envrionment */
To check if a variable is already defined, you can use the following code:
/* check if a variables is defined in the environment .environment */ globalVar = 'BS.MYVAR' if .environment[globalVar] = .NIL then say 'Environment symbol "' || globalVar || '" is not defined.' else do say 'Environment symbol "' || globalVar || '" is defined;' say 'the value is "' || .environment[globalVar] || '".' end /* else */
Note: "To avoid conflicts with future REXX-defined entries, it is recommented that entries you place in the program local environment or in the global environment include a least one period in the entry name."
Use the directives ::REQUIRES and ::ROUTINE to implement external routines.
---------- * ----------
Use the keyword instruction RAISE to raise a condition in the calling routine. This is very handy to avoid endless return code evaluations.
Example:
/* ------------------------------------------------------------------ */ /* save this code in the file 'TEST1.CMD' */ parse source . . thisFile say 'This is ' || thisFile || '.' /* install an error handler for user condition 4 */ call on user 4 name ErrorRaised say 'Now calling TEST2.CMD. TEST2.CMD will raise an user condition ...' /* now call TEST2.CMD; test2.cmd will raise an */ /* user condition */ call TEST2.CMD say 'Now ending the test program.' exit /* ------------------------------------------------------------------ */ /* simple handler for the user condition 4 */ ErrorRaised: say say '*** start of user condition handler ***' say say 'Condition "' || condition( 'C') || '" raised in line ' || , sigl || '.' say 'The condition description is "' || condition( 'D' ) || '".' say 'Additional information is "' || condition( 'A' ) || '".' say say '*** end of user condition handler ***' say return
/* ------------------------------------------------------------------ */ /* save this code to TEST2.CMD */ parse arg thisArgs parse source . . thisFile say ' This is "' || thisFile || '".' say ' Called with "' || thisArgs || '".' /* raise a user condition to the caller */ raise user 4 , Description 'This is a user error' , Additional 'This is additional information' exit /* ------------------------------------------------------------------ */
---------- * ----------
And there are a lot of very useful new functions in the REXXUTIL.DLL. See New REXXUTIL functions in Object REXX for an overview of the new functions. This section also contains information on how to use the new REXXUTIL.DLL with Classic REXX.
Again, read the section MIGRATION in the Online-Help of Object-Oriented REXX -- else you might miss some of the new features.