Your script can call another REXX script, just as if that other script were a built-in function, or a function/subroutine in your own script, passing it arguments and receiving back some value. In order to do this, the other script should be located in the current directory or in your scripts path (as set by Reginald's REXX Administration Tool). It is recommended that the name of the script end with a .rex extension.

Note: If using REXX Dialog, then you should call any external script or macro as described in running a child RXDLG script.

For example, if you want to call (from your main script) another script named foobar.rex, then you can call it as a function "value = foobar()" or a subroutine "CALL foobar". Whether you call it as a function or subroutine depends upon whether that script is supposed to return a value which you intend to use. When you call another script, it is referred to as an External Function/Subroutine (as opposed to internal ones which are contained inside of your main script).

If you know the exact (ie, full) name of the script with its path and extension, then you can specify that. For example, assume that the script you wish to call is named "MyScript.cmd" in the directory "C:\REXX". You may call it (as a function) as so:

/* Call the script as a function and get its return value */
myreturn = 'C:\REXX\MyScript.cmd'()

This can eliminate potential problems with two scripts having the same name but different extensions, or two scripts having the same name but located in different directories. Note that I placed quotes around the full script name. This prevents a SYNTAX error, because without the quotes, the unquoted colon would otherwise cause REXX to misinterpret F as a label name.

Note: If using REXX on an operating system with case-sensitive filenames, then you should always put quotes around the script's name, even if you don't specify its full name, and make sure that you type it exactly as its name appears on disk. Windows does not use case-sensitive filenames, but UNIX does.


Variables

It is not possible for a called script to access any of your script's variables. It is as if the called script had a PROCEDURE instruction at the beginning of it. But, you can pass arguments to that script, just as you would any internal function/subroutine. (The called script accesses those arguments via PARSE ARG or ARG(), just like your script accesses arguments passed by the user when he runs your script). Your script can also use the current Data stack to exchange items with a called script.

Note: Reginald supports using the PROCEDURE EXPOSE statement at the top of the called script. You can then list the variables in the calling script that you wish to be able to access in the called script.


Passing arguments and returning a value

The called script can use either the RETURN instruction, or the EXIT instruction to return to your script. The EXIT instruction may be used to return any data to your script in the same way as RETURN, but the called script can use EXIT to immediately return even when deeply nested in calls to its own subroutines.

Let's take an example of a script we'll call. It will be contained in a file named "foobar.rex". All foobar.rex does is check how many arguments were passed to it, and then it SAY's each argument. Finally, it returns the string The End to its caller.

/* Check how many args were passed from the script that called me */
args = ARG()

/* SAY those args */
DO i = 1 to args
   SAY ARG(i)
END

/* Return 'The End' to the script that called me */
RETURN 'The End'
Now let's examine a script that calls foobar.rex. We could call it as a function:
/* Call foobar as a function, passing 3 args, and get its return */
message = foobar('Arg 1', 100, MyVariable)

/* SAY its return */
SAY message
Note that if foobar.rex didn't return any value (after its RETURN keyword), then REXX would raise a SYNTAX condition with error number 45.1 in our main script, since we called foobar as a function and are attempting to use its return value.

Note: Reginald allows using the CALL keyword when you call a script as a function. So, we can do the following to throw away any return value from foobar, and no SYNTAX condition is raised if foobar doesn't return a value anyway:

/* Call foobar as a function, passing 3 args, and throw away its return */
CALL foobar('Arg 1', 100, MyVariable)

Or we can call foobar as a Subroutine. In this case, foobar doesn't have to return a value. but if foobar does, that value is stored in the special variable named RESULT. Otherwise, RESULT is DROP'ed.

/* Call foobar as a subroutine, passing 3 args, and check for its return */
CALL foobar 'Arg 1', 100, MyVariable

/* SAY its return */
IF SYMBOL('RESULT') == 'VAR' THEN SAY RESULT
ELSE SAY "foobar did not return a value"

Using the data stack to exchange data

We can also use the Data stack to exchange data between scripts. When a script calls another script, the child script shares (and has access to) the parent script's stacks. The child script's current stack is whatever stack the parent has set as its current stack.

Here is how our main script would pass those 3 arguments as items on the current stack, and then check if foobar has also returned any items on the stack. (It is expected that foobar will empty out the current stack if it has no items to return).

/* QUEUE 3 items for foobar */
QUEUE 'Arg 1'
QUEUE 100
QUEUE MyVariable

/* Call foobar as a subroutine */
CALL foobar

/* Check if foobar has returned anything on the stack */
DO WHILE QUEUED() > 0
   PARSE PULL line
   SAY line
END
And here is foobar, modified to read its args off of the current stack, and return items on the same stack.
/* Check if caller has passed anything on the stack, and SAY those items */
DO WHILE QUEUED() > 0
   PARSE PULL line
   SAY line
END

/* QUEUE 2 items to return to the caller */
QUEUE 'Item 1'
QUEUE 'Item 2'

/* Return to caller */
RETURN