You can write your own Functions (ie, subroutines) in REXX, placing them in your script. To identify a Function in your script, you need to put a label at the start of it.
Just like you can pass arguments to a built-in function such as TIME(), you can also pass arguments to your own REXX functions by putting those arguments between parentheses. And your Function can return one value to the instruction where it was called.
The following script contains a REXX Function named square, and calls it with some data (ie, passes it one argument). The square Function simply squares that argument and returns the result as its value.
/* Call the Function square() with 1 argument, * and print the Function's returned value. */ SAY "Three squared is" square(3) || '.' SAY "Five squared is" square(5) || '.' SAY "Nine squared is" square(9) || '.' EXIT /* Here's a function to square its argument. */ square: myarg = ARG(1) RETURN myarg * myargThe output of this script is:
Three squared is 9. Five squared is 25. Nine squared is 81.When REXX encounters the Function call square(3), it searches the script for a label called square. It finds that label on line 10 in the above example. REXX then executes the instructions starting at that line, until it encounters a RETURN instruction, whereupon it resumes executing the instruction that originally called the Function, replacing that Function call with the returned value (ie, square(3) gets replaced by its returned value of 9, and that's what the SAY instruction displays).
Note:
The EXIT instruction in the above script causes REXX to finish executing at that line instead of running down into the Function. It's important to make sure that you don't let REXX accidentally drop down into Functions at the bottom of your script. It's best to put all your Functions at the bottom of the script, and place one EXIT (or RETURN) above them all. Note that a Function can never accidentally drop down into another Function below it as long as it ends with RETURN, since RETURN jumps back to the instruction that originally called the Function.Your Function can obtain any arguments passed to it by using PARSE ARG in the same way as you optained arguments passed to your script. Alternately, you can use the ARG built-in function to determine how many args are passed, and to fetch each arg. In the above example, we used ARG() to get the passed argument into a variable named myarg. So, for the call square(3), myarg is assigned the value 3. A third way to get at the arguments passed to your function is by using USE ARG, USE ARG allows you to modify the values of any actual variables that were passed to your function, and those changes are reflected in whomever called your function.
When the RETURN instruction is reached, the expression specified after RETURN is evaluated and used as the return value of the Function. The expression can be a variable's value, mathematical expression, or even the return from another Function.
You can have more than one RETURN instruction in a Function, each returning a different value. But, REXX always jumps out of a Function immediately when it encounters a RETURN, and returns to the instruction that called the Function. Here we have two ways out of the Function answer(), plus two different possible return values:
/* Here's a Function that returns 1 if the user answers * "YES", or 0 if he answers anything else. */ answer: SAY "Do you want to learn REXX programming?" PARSE PULL ans IF ans == "YES" THEN RETURN 1 RETURN 0A Function can take multiple arguments, which are each separated with a comma, as so:
/* Call a Function with 3 arguments */ SAY "The results are:" query(5,"Yes","No") query(10,"X","Y") EXIT query: /* If the first argument is less than 10, return the second, * otherwise return the third. */ PARSE ARG c,x,y . IF c < 10 THEN RETURN x RETURN yThe output of the above script is:
The results are: Yes NoHere's another example where we pass several args to a function, using commas to separate them:
/* Call a Function named print to print args passed to it. */ CALL print("This is arg 1", arg 2, TIME()) EXIT print: /* Print the args. */ count = ARG() DO i = 1 TO count SAY ARG(i) END RETURNNote that "arg 2" is printed out as "ARG 2" because it is not enclosed in quotes when we pass it to print so REXX regards it as a variable named ARG (whose value happens to be its own name in this case), followed by a space and a 2.
Note: A Function need not return a value. (ie, It need not supply a value after the RETURN keyword). In this case, if you attempt to use the return value, such as to SAY it, you'll get a SYNTAX error.
Overriding built-in/add-on functions
You may need to be careful that the name you use for your function isn't the same as some built-in Function, or an add-on function in a DLL. If it is then it will override the built-in/add-on Function. That is to say that REXX will call your REXX function instead of the built-in/add-on.
For example, here we have a script that has a REXX Function in it named TIME:
/* Call a Function named TIME. */ SAY TIME() EXIT time: RETURN "It's time!"When you run this script, it doesn't display the current time. Rather, it displays the string It's time!. We have overridden the built-in Function named TIME(). (And remember that label/Function names are case-insensitive when not quoted, so even though we call TIME(), it matches our label name time).
It is possible to call a built-in/add-on Function even if you have your own REXX Function with the same name. You simply need to put quotes around the Function name when you call it, if you want the built-in/add-on function.
SAY TIME() EXIT time: /* Call the built-in Function TIME(). */ time = 'TIME'() /* If it's 12PM, then return "It's time!". */ IF time = '12:00pm' THEN RETURN "It's time!" RETURN timeIn the above script, our first instruction calls our own REXX Function named TIME. But within that Function, we call the built-in TIME() Function to get the current time, by putting quotes around the Function name.
Note: When you put quotes around a Function name, then case becomes sensitive, and you should therefore use all capital letters (ie, 'TIME', not 'Time' nor 'time', etc). Otherwise, REXX may report that it can't find a script by that name.
Note: If you wish to check whether some Function name in your script is the same as some built-in function, or add-on DLL function, then call RxFuncCheck() after you have registered all add-on DLLs.
Finding the name of who called your Function
Reginald's UNAME() function can be used to tell who called your subroutine/function. Pass a value of 2 to get the name. For example, if your subroutine/function was called by another subroutine named "A_Sub" then:
SAY "I was called by" UNAME(2)...will display I was called by A_Sub.
Note: If who called your subroutine was the main body of the script, then UNAME(2) will give you the name of the script itself. (ie, It's tantamount to doing a PARSE SOURCE instruction, and then parsing out the script name).
In fact, if you call UNAME(2) from the main body of your script, then you can discover the name of the subroutine (in the parent script) that called your script.
If you want to get the name of the current subroutine that you're in, then pass a 1 to UNAME. For example, assume that two different subroutines both SIGNAL to the same place. You can find out which one jumped there as so:
Sub1: SiGNAL Here Sub2: SiGNAL Here Here: SAY UNAME(1) "SIGNAL'ed here."Passing a -1 will give you the name of the current script. (It's tantamount to PARSE SOURCE). Passing a -2 will give you the name of the script that called your script (or an empty string if your script was directly run by REXX Script Launcher, or Rexx Programmer Center, or some other program).
Note: The script name that UNAME() returns may not be fully-qualified, nor end in a REXX extension. Scripts that are embedded inside of a Macro DLL or EXE (ie, created with REXX Programmer Center) will not have a path, and may or may not have any extension. Only disk-based scripts have a full path.
Calling a function whose name is stored in a variable
Reginald allows you to call a Function whose name is stored in a variable. To do this, use the CALL keyword, followed by the variable name inbetween brackets:
/* Call a Function named print to print args passed to it. */ My_Function = "print" CALL [My_Function]("This is arg 1", arg 2, TIME()) EXITOne limitation of the above is that the function's return value is automatically discarded.