Functions and subroutines are called in the same way. The only difference between functions and subroutines is that functions must return data, whereas subroutines need not.
The following types of routines can be called as functions:
If the routine name exists as a label in the program, the current processing status is saved for a later return to the point of invocation to resume execution. Control is then passed to the first label in the program that matches the name. As with a routine called by the CALL instruction, status information, such as TRACE and NUMERIC settings, is saved too. See the CALL instruction (CALL) for details.
If you call an internal routine as a function, you must specify an expression in any RETURN instruction so that the routine can return. This is not necessary if it is called as a subroutine.
/* Recursive internal function execution... */ arg x say x"! =" factorial(x) exit factorial: procedure /* Calculate factorial by */ arg n /* recursive invocation. */ if n=0 then return 1 return factorial(n-1) * n
FACTORIAL is unusual in that it calls itself (this is recursive invocation). The PROCEDURE instruction ensures that a new variable n is created for each invocation.
These functions are always available and are defined in Built-in Functions.
You can write or use functions that are external to your program and to the language processor. An external routine can be written in any language, including Rexx, that supports the system-dependent interfaces the language processor uses to call it. You can call a Rexx program as a function and, in this case, pass more than one argument string. The ARG, PARSE ARG, or USE ARG instruction or the ARG built-in function can retrieve these argument strings. When called as a function, a program must return data to the caller.
Calling an external Rexx program as a function is similar to calling an internal routine. For an external routine, however, the caller's variables are hidden. To leave the called Rexx program, you can use either EXIT or RETURN. In either case, you must specify an expression.
You can use the INTERPRET instruction to process a function with a variable function name. However, avoid this if possible because it reduces the clarity of the program.
Functions are searched in the following sequence: internal routines, built-in functions, external functions.
Function calls or subroutines may use a name that is specified as a symbol or a literal string. For example, these calls are equivalent:
call MyProcedure call 'MYPROCEDURE'
Note that the name value when specified as a symbol is the symbol name translated to upper case. Both of the calls above will search for a routine named "MYPROCEDURE". When the name is specified as a literal string, then the literal string value is used as-is. Thus the following two calls are not equivalent:
call MyProcedure -- calls "MYPROCEDURE" call 'MyProcedure' -- calls "MyProcedure"
Some steps of the function and subroutine search order are case sensitive, so some care may need to be exercised that the correct name form is used:
Internal routines. Normally, labels are specified as a symbol followed by a ":". These labels have a name value that's all uppercase. Since unquoted (symbol) names also have uppercase values, these will match easily. It is also possible to use literal strings for label names. If these labels contain lowercase characters, they will not be located using normal call mechanisms
Built-in functions. The built-in function names are all uppercase, so using a mixed-case literal string built-in function name will fail to locate the function.
x = wordPos(needle, haystack) -- calls "WORDPOS", which works x = "wordPos"(needle, haystack) -- calls "wordPos", which will fail
External routines. Some steps of the external function search order may be case sensitive, depending on the system. This may occasionally require a function or subroutine name to be specified as a mixed case literal string to be located.
If the call or function invocation uses a literal string, then the search for internal label is bypassed. This bypass mechanism allows you to extend the capabilities of an existing internal function, for example, and call it as a built-in function or external routine under the same name as the existing internal function. To call the target built-in or external routine from inside your internal routine, you must use a literal string for the function name.
/* This internal DATE function modifies the */ /* default for the DATE function to standard date. */ date: procedure arg in if in="" then in="Standard" -- This calls the DATE built-in function rather than recursively -- calling the DATE: internal routine. Note that the name needs to -- be all uppercase because built-in functions have uppercase names. return "DATE"(in)
Since built-in functions have uppercase names the literal string must also be in uppercase for the search to succeed.
External functions and subroutines have a system-defined search order.
The search order for external functions is as follows:
Functions defined on ::ROUTINE directives within the program.
Public functions defined on ::ROUTINE directives of programs referenced with ::REQUIRES.
Functions that have been loaded into the macrospace for preorder execution. (See the Open Object Rexx: Programming Guide for details.)
Functions that are part of a function package or library package. (See the Open Object Rexx: Programming Guide for details.)
Rexx functions located in an external file. See below for how these external files are located.
Functions that have been loaded into the macrospace for postorder execution.
Rexx uses an extensive search procedure for locating program files. The first element of the search procedure is the locations that will be checked for files. The locations, in order of checking, are:
The same directory the program invoking the external routine is located. If this is an initial program execution or the calling program was loaded from the macrospace, this location is skipped. Checking in this directory allows related program files to be called without requiring the directory be added to the search path.
The current filesystem directory.
The current filesystem directory.
Some applications using Rexx as a scripting language may define an extension path used to locate called programs. If the Rexx program was invoked directly from the system command line, the no extension path is defined.
Any directories specified via the REXX_PATH environment variable.
Any directories specified via the PATH environment variable.
The second element of the search process is the file extension. If the routine name contains contains at least one period, then this routine is extension qualified. The search locations above will be checked for the target file unchanged, and no additional steps will be taken. If the routine name is not extension qualified, then additional searches will be performed by adding file extensions to the name. The following extensions may be used:
If the calling program has a file extension, then the interpreter will attempt to locate a file using the same extension as the caller. All directory locations will be checked for a given extension before moving to the next potential extension.
Some applications Rexx as a scripting language may define extensions that should be added to the search order. For example, an editor might define a preferred extension that should be used for editor macros. This extension would be searched next.
The default system extensions (.REX).
If the target file has not been located using any of the above extensions, the file name is tried without an added extension.
There are some system file system considerations involved when search for files. The Windows file system is case insensitive, so files can be located regardless of how the call is specified. Unix-based systems have a case sensitive file system, so files much be exact case matches in order to be located. For these systems, each time a file name probe is attempted, the name will be tried in the case specified and also as a lower case name.
If an external or built-in function detects an error, the language processor is informed, and a syntax error results. Syntax errors can be trapped in the caller using SIGNAL ON SYNTAX and recovery might be possible. If the error is not trapped, the program is ended.