procedureNameLabel : [ procedure [ expose variableList ] ]
where: variableList is one or more symbol names separated by spaces. When a symbol name is enclosed in parentheses it is a special reference variable as described in the variable reference section below. |
The procedure instruction begins an internal procedure. The instruction is preceded by a label, which is the name of the internal procedure. Multiple labels can precede the procedure. It is not necessary to specify a procedure instruction, as will be described below.
Warning: if execution inadvertently flows into a procedure instruction, this will cause error code 17 (unexpected procedure) to be raised. This problem is generally corrected by preceding the procedure instruction's label(s) by an return or exit instruction. This problem can also occur when a signal instruction transfers to an incorrect label.
When execution inadvertently flows into a procedure that lacks a procedure instruction an unexpected procedure WILL NOT be raised. This can lead to a variety of unexpected consequences. When this type of procedure is initially created you are strongly advised to precede the associated label(s) with an return or exit instruction.
The procedure instruction is not used in the initial program of an external procedure. Consequently, variables of the calling program are never exposed within external procedures.
Click here to review the structure of Rexx programs and procedures.
You can use three different methods of exposing calling program variables in internal procedures. This is controlled by using the optional procedure instruction, which can be followed by an optional list of specific variables to expose.
If the procedure instruction is specified, without an exposed variable list, then none of the calling program's variables are exposed. Since variables aren't exposed, the internal procedure can only obtain arguments from the calling procedure, and return a result.
Hint: you will generally want to expose none of the calling program's variables, because this is the least problematic method.
/* main program */ do n=1 to 5 say 'The factorial of' n 'is:' factorial( n ) end return factorial : procedure /* none of the caller's variables are exposed */ n = arg(1) /* n is a local variable */ if n = 1 then return 1 x = n n = n - 1 /* ok, this is a local variable */ return x * factorial( n ) |
Hint: you should generally AVOID the exposure of all variables, because it is VERY problematic. When this type of procedure is initially created you are strongly advised to precede the associated label(s) with an return or exit instruction.
abc : /* all of the caller's variables are exposed -- YIKES !! */ parse arg a, b, c ...procedure logic appears here ...reference caller variables as necessary ...modify caller variables as a last resort return 1 /* return a result to the caller */ |
When the internal procedure is merely referencing the values of the calling program's variables, then no problems should ensue. However, if the internal procedure m-o-d-i-f-i-e-s a variable, it might be revising a calling program's variable, even if it looks like the variable is local to the procedure.
The following shows how the absence of a procedure instruction can lead to an infinite loop.
/* main program */ do n=1 to 5 say 'The factorial of' n 'is:' factorial( n ) end return factorial : /* all of the caller's variables are exposed -- YIKES !! */ if n = 1 then return 1 x = n n = n - 1 /* this is the caller's variable n -- ugh. */ return x * factorial( n ) |
There are three types of variables that can be exposed.
Beware: the order of variable exposure can be significant. Assume N is 7 in the calling program. If array.N is exposed, and N was previously exposed in the expose clause, then the variable that is exposed is ARRAY.7 . However, if N was not previously exposed in the expose clause then the variable that is exposed is ARRAY.N .
Hint: you should expose specific variables cautiously, because it is somewhat problematic.
The following shows how the the misuse of an exposed variable can lead to an infinite loop.
/* main program */ do n=1 to 5 say 'The factorial of' n 'is:' factorial( n ) end return factorial : procedure expose n /* only exposing n seems to be ok */ if n = 1 then return 1 x = n n = n - 1 /* oops, this is the caller's variable n -- ugh. */ return x * factorial( n ) |
Within the list of variables that follows the expose keyword, one additional capability can be used. A variable name can be enclosed in parentheses. This exposes the specific variable, and addition its value is used as a subsidiary list. The list consists of a series of variable names separated by spaces. These are exposed in sequence left to right. The names that are exposed follow the same rules as the original exposure list -- i.e. how simple, compound, and stem variables are exposed.
Note: the variable reference capability was added in language level 4.0 (TRL-2). Thus, this capability may be absent in some Rexx implementations.
Here is a simple example of the use of a variable reference in a procedure instruction's expose clause.
/* main procedure */ varlist = 'a b c' call getvalues say a b c return 0 getvalues : procedure expose (varlist) parse value 'abra ca dabra' with a b c return |
Here is a detailed example of a program that uses exposed variables in various procedures
/* TopoSort.rex topological sort identifies the order of dependences from most dependent to least dependent usage rexx TopoSort [MixedCase] < infile > outfile example input file -- a series of dependency definitions: +-------+ | a b | | b c | | b e | | c d | | b f | | m n | | n o | | o p | | m r | | m s | +-------+ corresponding output file a tree showing levels of dependency: +-------------+ |A | |+ B [A] | |++ C [B] | |+++ D [C] | |++ E [B] | |++ F [B] | |M | |+ N [M] | |++ O [N] | |+++ P [O] | |+ R [M] | |+ S [M] | +-------------+ */ if translate( left( arg(1), 1 ) ) = 'M' then characterCase = 'M' else if arg(1) <> '' then do call lineout !, 'Usage' call lineout !, ' rexx TopoSort [MixedCase] <infile >outfile' exit 1 end else characterCase = 'U' /* default is Parentcase */ /* use parse to do multiple variable assignment * nItems = 0 * nLevels = 0 * itemDepth. = 0 * items = '' * dependsOn. = '' * dependeeOf. = '' * shownItems = '' * prevFrom = '' */ parse value 0 0 0 with , nItems nLevels itemDepth. , items dependsOn. dependeeOf. shownItems prevFrom do lines() if characterCase = 'U' then parse UPPER linein From To . else parse linein From To . if From = To | DetectCycle( To ) then do call lineout !, 'Cycle detected.' From '<->' To'...' exit 1 end dependsOn.From = dependsOn.From To dependeeOf.To = dependeeOf.To From call ReviseDepth From To call AddItem From call AddItem To if From <> prevFrom then do /* call lineout !, From */ prevFrom = From end end /* end lines() loop */ do i=nLevels to 0 by -1 replicas = items do while replicas <> '' parse var replicas From replicas if itemDepth.From = i then call ShowItem From '_' end end exit 0 |