Subroutines
Overview
User Language lets you treat a single set of statements as a simple or complex subroutine. You can:
- Execute simple subroutines a number of times from different locations within a request.
- Use complex subroutines as you would simple subroutines.
In addition, you can:
- Pass parameters via parameter lists.
- Declare variables locally.
Common elements
An element that is not passed as a parameter can be shared between complex subroutines, or between a complex subroutine and the main request, when you declare that element as COMMON.
ON units
An ON unit specifies a response action to a triggering event: for example, the terminal operator pressing one of the ATTENTION identifier (AID) keys. ON units let an application override the normal system response.
Simple subroutines
Outlining a simple subroutine
The following statements are used in simple subroutines within a request:
Statement | Purpose |
---|---|
CALL | Transfers control to the subroutine. |
END SUBROUTINE | Indicates the end of the subroutine. |
JUMP TO | Transfers control to another statement in the request. |
RETURN | Returns control to the statement immediately following the CALL statement. |
SUBROUTINE | Indicates the beginning of a subroutine. |
The statements are coded in the following sequence, so they are described in order of usage.
- SUBROUTINE statement
- JUMP TO and RETURN statements as appropriate
- END SUBROUTINE statement
- CALL statement
SUBROUTINE statement
A simple subroutine consists of a sequence of User Language statements that must begin with a SUBROUTINE statement. The body of the subroutine is composed of the statements immediately following the SUBROUTINE statement.
Syntax
The format of the SUBROUTINE statement is:
label: SUBROUTINE
A simple subroutine must be labeled; without a label it cannot be executed. You cannot use field names in the subroutine, except within a FOR loop, even if the subroutine is always called from within a record loop.
Example of a simple subroutine
The following request uses two subroutines.
Note: Simple subroutine statements in the following example are described more fully after this example.
The first subroutine, TOTAL, accumulates a total in the %variable named %TOTAL.MUSTANGS. The second subroutine, PRINT.TOTAL, prints a literal that varies depending on the total accumulated.
BEGIN MUSTANGS: FIND ALL RECORDS FOR WHICH MODEL = MUSTANG END FIND FOR EACH RECORD IN MUSTANGS CALL TOTAL END FOR CALL PRINT.TOTAL TOTAL: SUBROUTINE %TOTAL.MUSTANGS = %TOTAL.MUSTANGS + 1 RETURN END SUBROUTINE TOTAL PRINT.TOTAL: SUBROUTINE IF %TOTAL.MUSTANGS LE 10 THEN PRINT 'TOTAL MUSTANG POLICIES - NOT GREATER THAN 10' ELSEIF %TOTAL.MUSTANGS LE 50 THEN PRINT 'TOTAL MUSTANG POLICIES - NOT GREATER THAN 50' ELSEIF %TOTAL.MUSTANGS GT 50 THEN PRINT 'TOTAL MUSTANG POLICIES - GREATER THAN 50' END IF RETURN END SUBROUTINE PRINT.TOTAL END
JUMP TO statements
Model 204 compiles subroutines only after they are called, regardless of where they are in your program.
If you use a JUMP TO statement, its destination must be within the same subroutine. You cannot jump into a subroutine from outside of a subroutine.
RETURN statement
The RETURN statement returns control from the subroutine to the statement following the most recent CALL statement.
Syntax
The format of the RETURN statement is:
RETURN
The RETURN statement can appear only within the body of a subroutine. Use of the RETURN statement inside of an ON unit terminates compilation, the procedure cannot run, and the following message is produced:
M204.1779 RETURN IS INVALID IN ON UNITS, USE BYPASS STATEMENT
Model 204 automatically generates a return at the end of a subroutine, if you do not specify one.
For more information, see ON units and Passing control to and from ON units.
END SUBROUTINE statement
You can end a subroutine using an END SUBROUTINE statement or an END BLOCK statement.
Syntax
The END SUBROUTINE statement is formatted as follows:
END SUBROUTINE [label]
CALL statement
The CALL statement transfers control to the subroutine.
Syntax
The format of the CALL statement is:
CALL label
Example
This statement:
CALL.SUB: CALL COMPUTE.PREMIUM
transfers control to the following statement:
COMPUTE.PREMIUM: SUBROUTINE
When you use a simple subroutine, no arguments are passed in subroutine calls. Instead, input and output values are implicitly passed in %variables or global variables. If you want field values as arguments, you must assign the values to %variables before the call, or the subroutine must contain its own FOR EACH RECORD loop on the set of records to be processed.
Processing continues sequentially from that statement until another control transfer statement — a CALL, IF, JUMP TO, RETURN, or STOP statement — is encountered.
Complex subroutines
Complex subroutines perform all the operations of a simple subroutine. In addition, a complex subroutine can pass parameters via parameter lists and can declare local variables.
Note: The format of the SUBROUTINE statement changes for a complex subroutine and the CALL statement is also more complex.
Symbolic parameter passing
You can specify positional parameters on the CALL statement for a complex subroutine. These parameters are substituted for symbolic parameters used within the subroutine during execution. You can use the following elements as parameters to a complex subroutine:
- Scalar %variables
- %variable arrays
- Lists of records
Local variable declaration
Labels, %variables, and other elements in a complex subroutine are local to the subroutine; they do not conflict with elements of the same name in the request or in other complex subroutines. This allows the same element to be used in different portions of a request, and for the same name to describe different elements in each portion.
Processing
All data within the subroutine is statically allocated. When the subroutine is called, the values of all found sets, counts, noted values, and variables are as they were when the subroutine was last executed. Recursive calls to the same subroutine do not allocate separate storage for each subroutine local variable. Local variables are allocated only one time.
OPEN statement within a complex subroutine
A User Language OPEN statement — an OPEN statement inside a BEGIN/END — when used within a complex subroutine on a file that is not defined to the region produces the following counting error and the request is not compiled:
M204.1521: entity-name DOES NOT EXIST OR REQUESTED ACCESS NOT AUTHORIZED
SUBROUTINE statement for complex subroutines
Complex subroutines begin with the following form of the SUBROUTINE statement:
SUBROUTINE subname [(formal-parameter [inout-option] [, ...])]
Where:
subname is the name of the subroutine.
formal-parameter is the declaration of a symbolic parameter used within the subroutine. The parameter list is comprised of the formal-parameter and subsequent input-options. The parameter list is enclosed in parentheses, and it contains declarations for each parameter separated by commas. The parameter list must be specified on one logical line. Null parameters are not allowed.
formal-parameter specifies the name and type of element to be used within the subroutine and can be any of the following:
- Scalar (nonarray) %variable formatted as:
- Array %variable formatted as:
- A list of records formatted as:
%variable [IS] {[STRING] [LEN] n [DP {n | *}] | [FIXED [DP n] | FLOAT]}
For example:
SUBROUTINE EXAMPLE (%W IS STRING DP * - %X IS STRING LEN 40, - %Y IS STRING LEN 10 DP 2, - %Z IS FLOAT, - %A IS FIXED DP 4)
%variable [IS] [[STRING] [LEN n] [DP {n | *}] [ARRAY(* [,*[,*]])] [NO FIELD SAVE] | {FIXED [DP n] | FLOAT} [ARRAY(* [,*[,*]])]]
The number of dimensions must be specified in the subroutine declaration, but the exact size of each dimension is not specified. An asterisk (*) is used instead of the dimension size. For example:
SUBROUTINE EXAMPLE(%ARR IS STRING LEN 10 - ARRAY(*,*))
[LIST] listname [IN [FILE | [PERM | TEMP] GROUP]] name
The context of the list is restricted to a single file or group. For example:
SUBROUTINE GENERAL(LIST TOTRECS IN FILE VEHICLES)
inout-option specifies whether a formal-parameter is to be used as input to the subroutine or passed as output from the subroutine. Options are as follows:
Option | Data can be... |
---|---|
INPUT (the default) |
Passed into, but not out of, a subroutine. In addition, data conversions are performed automatically, in the same manner as an assignment statement. All references to INPUT parameters that update the parameter, for example, assignment statements, result in compiler errors. |
INPUT OUTPUT | This option is equivalent to the OUTPUT option. |
OUTPUT |
Returned from a subroutine. No data conversions are performed. The lengths of complex subroutine string OUTPUT parameters are inherited from calling parameters. If the lengths are specified in the subroutine declaration, the following message is written to the audit trail: M204.1981 LENGTH IGNORED FOR OUTPUT PARAMETER Compilation and evaluation of the procedure continues, with the inherited length. |
END SUBROUTINE statement
Like simple subroutines, a complex subroutine ends with an END SUBROUTINE statement. However, unlike simple subroutines, you must not label the SUBROUTINE statement.
All declarations and statements compiled after the SUBROUTINE statement become a part of the subroutine until the END SUBROUTINE statement is encountered or until the request is ended with the END statement.
CALL statement
The execution of a CALL statement passes control to a complex subroutine. The first time a subroutine is called, all elements in the subroutine are initialized. Subsequently, each time the subroutine is called, all elements in the subroutine remain in the same state as they were when the subroutine was last exited. You are responsible for reinitializing the elements within the subroutine.
Syntax
A complex subroutine is invoked with this form of the CALL statement:
CALL subname [(actual-parameter [, ...])]
Where:
subname is the name of the subroutine.
actual-parameter specifies the actual data that replaces the symbolic formal-parameter within the subroutine. At execution time, the value of each actual-parameter is substituted, based on the order in which it appears, for a formal-parameter declared for the subroutine. The correspondence of each formal-parameter in the SUBROUTINE statement to an actual-parameter in the CALL statement is by position rather than by name.
An actual-parameter can be any of the following:
- %variable or expression
- Array %variable
Expressions can contain field names, screen items, image items, and constants.
When an entire array is passed as a parameter, the %variable name is used with the percent (%) sign, but no parenthesis or subscript expressions follow it. For example:
BEGIN %A IS FLOAT ARRAY(10,10) DECLARE SUBROUTINE FDO(FLOAT ARRAY(*,*)) . . . CALL FDO (%A) . . . SUBROUTINE FDO (%B IS FLOAT ARRAY(*,*)) PRINT %B(1,1) END SUBROUTINE END
The $Arrsize function can be used to determine the number of elements in a particular dimension of an array.
< li>A listAn actual-parameter can be a list, optionally preceded by the word LIST to distinguish it from a field name.
If the compiler already knows of the formal-parameter list because the subroutine is already compiled or declared, the LIST keyword is unnecessary.
If the list has never been declared in the request, a compilation error results.
Additional rules for parameters
The following additional rules apply to actual-parameters and parameter passing:
- The maximum number of parameters that can be passed in complex subroutines is 63.
- If the formal-parameter is specified as an OUTPUT parameter, the corresponding actual-parameter must match type.
- The INPUT parameters of a %variable array requires that the type of the array — FLOAT, FIXED, or STRING — the DP specification, and the number of dimensions match the actual-parameter. In addition, if the NO FIELD SAVE (NO FS) option is specified for a STRING formal-parameter, it also must be specified for the corresponding actual-parameter and vice versa. All checking of array bounds within subroutines occurs during evaluation.
- The INPUT parameters of a list requires the same file or group context as the actual-parameter.
- An INPUT parameter of a scalar %variable can be called with an actual-parameter that is an expression or %variable of a differing type. Model 204 converts the actual-parameter to the type required by the subroutine, according to the rules of the assignment statement. The converted value has a separate storage location from the original variable or expression.
- OUTPUT parameters of lists and %variable arrays follow the same rules as INPUT parameters for lists and %variable arrays.
- OUTPUT parameters of scalar %variables require that the actual-parameter supplied in the CALL statement be another scalar %variable (no constants or expressions) of the same type — FLOAT, FIXED, or STRING — and that the number of decimal places be the same, or the following message is issued by Model 204:
Passing arguments to INPUT parameters might involve truncation where the number of decimal places is different, or conversion to 0 for non-numeric strings.
M204.1725 PARAMETER NUMBER n IS TYPE INCOMPATIBLE
Strings can be of any length. The length used is that of the actual input parameter.
Processing complex subroutines
Checking for the validity of parameter lists occurs during compilation. When the SUBROUTINE statement is compiled, the types of parameters are saved by the compiler, and any subsequent CALL statements are verified as to whether the actual parameter is consistent with the type of formal parameter.
When a CALL statement is compiled and the subroutine to which it refers has not been compiled, the compiler verifies whether the actual parameter list is compatible with other CALL statements for the subroutine that it has already compiled.
The compiler cannot verify that the actual parameters are compatible with the SUBROUTINE statement until that SUBROUTINE statement is compiled. If the compiler then detects a CALL statement that is not compatible with the SUBROUTINE statement, an error is issued at that time. Checking is done at compile time, so a statement that is not executed during evaluation can still generate an error.
DECLARE SUBROUTINE statement
The DECLARE statement for a complex subroutine is similar to the SUBROUTINE statement, except that the parameter names are omitted. The complete syntax for the DECLARE SUBROUTINE statement is:
Syntax
DECLARE SUBROUTINE subname [(type [inout-option] [, ...])]
Where:
subname is the name of the subroutine.
type is one of the following:
- Scalar %variable of the following format:
{STRING [LEN] n [DP {n | *}] | [FIXED [DP n] | FLOAT]}
- Array %variable of the following format:
{STRING [LEN n] [DP [n | *}] [ARRAY(* [,*[,*]]) [NO FIELD SAVE]] | [FIXED [DP n] | FLOAT] [ARRAY(* [,*[,*]])]}
- A list of records of the following format:
[LIST] [IN {FILE | [PERM | TEMP] GROUP} name]
inout-option specifies whether each formal parameter is to be used as input to the subroutine or passed as output from the subroutine. Options are the same as those that can be specified on the SUBROUTINE statement: INPUT, OUTPUT, or INPUT OUTPUT.
Usage
Issue: When a CALL statement refers to a subroutine that has not yet been compiled, the error checking capabilities of the compiler are limited, because the number and type of formal parameters are not known to the compiler. If the first CALL statement for a particular subroutine is not correctly coded, then a large number of error messages can be generated when compiling subsequent CALL statements, although these statements might be correctly coded.
Solutions: You can avoid this problem in one of the following ways:
- Declare the formal parameter list with the DECLARE statement. When the DECLARE statement precedes the first CALL, the compiler generates the correct error messages, if problems are encountered.
- Place the subroutine before the first CALL statement.
Specifying a list of records
If you specify a LIST (of records), you must also specify FILE or GROUP. For example:
OPEN METADATA password BEGIN DECLARE LIST ORIG IN METADATA DECLARE SUBROUTINE THE.SUB(LIST IN FILE METADATA INOUT) . . CALL THE.SUB (LIST ORIG) . . SUBROUTINE THE.SUB(LIST ORIG IN METADATA INOUT) . . END SUBROUTINE THE.SUB END
If you do not specify FILE or GROUP in the code, you receive the following error message:
M204.1725: PARAMETER NUMBER n IS TYPE INCOMPATIBLE
Example
In the following example, note that the DECLARE statement lists the formal parameters, while the CALL statement lists the actual parameters:
DECLARE SUBROUTINE SUB1(FLOAT, FIXED DP 2, STRING, - STRING DP *) . . CALL SUB1(%INCREASE,%WAGES,%NAME,%DEPT) . . SUBROUTINE SUB1(%A IS FLOAT, %B IS FIXED DP 2, %C IS STRING, %D IS STRING DP *) END SUBROUTINE . . END
Impact on CALL and SUBROUTINE statements
The CALL and SUBROUTINE statements are not overridden when a DECLARE SUBROUTINE statement is present. The DECLARE SUBROUTINE statement does not cause the compiler to use more table space, nor does it alter the way in which the compiler output is generated.
Rocket Software recommends that you use a DECLARE SUBROUTINE statement before the CALL statement for a subroutine or that you place the subroutine itself before the CALL statement. Error reporting will be more complete and specific to the subroutine.
Exiting the subroutine
The RETURN statement returns control to the statement immediately following the most recent CALL statement. If an END SUBROUTINE or END statement is encountered without a RETURN statement, RETURN processing is implied and automatically added by the compiler. More than one RETURN statement can be specified in a subroutine.
If a JUMP statement is used within a subroutine, its destination must be within the same subroutine. You cannot jump into a subroutine.
A STOP statement, when executed inside a subroutine, terminates the request in the same manner as it does when executed outside a subroutine.
Examples using complex subroutines
This section provides examples of complex subroutines. In the following example, an employee name with regular and overtime hours is passed to the subroutine CALC.WAGES. The total pay is returned and a list of records processed by the routine is updated.
BEGIN DECLARE SUBROUTINE CALC.WAGES(FIXED DP 2, - FIXED DP 2, STRING, FIXED DP 2 OUTPUT, - LIST IN FILE EMPLOYEE INOUT) . . . CALL CALC.WAGES(%R.HRS, %OT.HRS, %NAME, - %TOTAL.PAY, LIST EMPLST) . . . SUBROUTINE CALC.WAGES(%REG IS FIXED DP 2, - %OT IS FIXED DP 2, - %E.NAME IS STRING, - %TOTAL IS FIXED DP 2 OUTPUT, - LIST EMPLIST IN FILE EMPLOYEE INOUT) %TEMP IS FIXED DP 2 WAGES1: IN EMPLOYEE FIND ALL RECORDS FOR WHICH NAME = %E.NAME END FIND FOR EACH RECORD IN WAGES1 %TEMP = (RATE * %REG) %TOTAL = %TEMP + (RATE * 1.5 * %OT) END FOR PLACE RECORDS IN WAGES1 ON LIST EMPLIST RETURN END SUBROUTINE END
In the following example, an entire array is passed as a single subroutine parameter:
BEGIN %NUMBERS IS FLOAT ARRAY(10,10) %MAX IS FLOAT DECLARE SUBROUTINE MAXIMUM(FLOAT ARRAY(*,*), - FLOAT OUTPUT) . . . CALL MAXIMUM(%NUMBERS,%MAX) . . . SUBROUTINE MAXIMUM(%ARR IS FLOAT ARRAY(*,*), - %M IS FLOAT OUTPUT) %I IS FIXED %J IS FIXED %M = 0 FOR %I FROM 1 TO $arrsize('%ARR',1) FOR %J FROM 1 TO $arrsize('%ARR',2) IF %ARR(%I,%J) GT %M THEN %M = %ARR(%I,%J) END IF END FOR END FOR END SUBROUTINE END
Sharing common elements
An element that is not passed as a parameter can be shared between complex subroutines, or between a complex subroutine and the main request, when it is declared as a common element. A common element is created by using the DECLARE statement or by using the COMMON keyword on a %variable IS declaration. For an element to be shared, it must be declared as common in every place (each separate scope) in which it is used.
Scope of elements
The scope of an element refers to the area within a request in which an element has a particular meaning. In User Language, complex subroutines have a different scope than the remainder of the request (the elements of a complex subroutine differ from the elements outside the subroutine even when they have the same name). This concept applies to labels, lists, %variables and %variable arrays, menus, screens, and images.
The elements of the main request (the statements not enclosed by any SUBROUTINE/END SUBROUTINE statements) and the elements of all simple subroutines share the same scope. Simple subroutines share the same elements with the main request and all other simple subroutines within the request.
The following elements can be shared if they are declared as common:
%variables and %variable arrays
You can share %variables if a DECLARE %variable or a %variable IS statement that declares the %variable as common is present in each portion of the request where you use the %variable. The %variable must have the same type, length, number of decimal places, and FIELD SAVE option in each declaration.
Lists of records
You can share a list if a DECLARE statement that declares the list as common is contained in each place where you use the list. The declaration of the list must precede the first reference to that list, or Model 204 declares the list implicitly without the common attribute.
Found sets
You can share found sets if the label of the FIND statement is declared as common. To effectively share a found set, follow these steps:
- Add a DECLARE statement that declares the statement label as common before the actual label in the portion of the request where the FIND statement will be executed.
- Add a DECLARE statement that declares the label as common to any parts of the request that require access to that found set. These parts, however, cannot contain a label with the same name, or a compiler error occurs.
After execution of the FIND statement, you can access the record set in any portion of the request that contains the proper DECLARE statement.
Menus and screens
You can share a menu or screen as common under the following conditions:
- The first reference to the menu or screen must be the complete definition of the menu or screen, with the COMMON keyword following the MENU or SCREEN statement.
- Other parts of the request can reference the same menu or screen by using an abbreviated declaration of the form:
DECLARE [MENU menuname | SCREEN screenname] COMMON
If the complete definition of the menu or screen exists, and if that definition was declared as common, the menu or screen is shared. If not, a compiler error results. Two or more complete menu or screen definitions with the COMMON keyword also result in a compiler error.
If you use a menu or screen %variable (:%variable) within a complex subroutine to refer to a COMMON screen element, you must include a common declaration for each possible value of the menu or screen name.
Images
The sharing of images follows the same rules used for the sharing of menus and screens. If multiple images are contained within the same block, then the following rules also apply:
- You can use the COMMON keyword on only the first IMAGE statement of a block. All other images within the same block are automatically considered as candidates for sharing as common data.
- All other parts of the request that access common images must contain the following abbreviated form of the DECLARE statement for each image to which access is required:
DECLARE IMAGE imagename COMMON
DECLARE statement
You can use the DECLARE statement to perform the following functions:
- Specify subroutine formal parameter types.
- Specify variables, labels, lists, menus, screens, and images as COMMON.
Menus and screens are discussed in detail in Full-screen feature.
Images are discussed in detail in Images.
- To declare lists without having to use an
IN filename CLEAR LIST listname
clause. See Creating and clearing a list. - To declare %variables, see Using variables and values in computation.
Syntax
The format of the DECLARE statement is as follows:
DECLARE declaration
where declaration is one of the following:
LABEL label COMMON [LIST] listname [IN [FILE [PERM | TEMP] GROUP] name] [COMMON] IMAGE imagename COMMON MENU menuname COMMON SCREEN screenname COMMON %variable [IS] {FIXED [DP n] | FLOAT} [ARRAY(d1 [,d2 [,d3]])] [COMMON] %variable [IS] STRING [LEN n] [DP {dn1 | *}] [ARRAY(d1 [,d2 [,d3]])] [NO FIELD SAVE] [COMMON] SUBROUTINE subname[(type [inout-option] [,...]) ]
Example
For example, the DECLARE statement declares the list RECNAMES in the following request:
BEGIN DECLARE LIST RECNAMES DECLARE SUBROUTINE REGION(LIST OUTPUT, STRING) . . . CALL REGION(LIST RECNAMES, 'NORTHEAST') . . . SUBROUTINE REGION(LIST RGNLST OUTPUT, - %REGION IS STRING) R1: IN EMPLOYEE FIND ALL RECORDS FOR WHICH REGION = %REGION R2: PLACE RECORDS IN R1 ON LIST RGNLIST END SUBROUTINE END
Defining common variables
You can define common variables at the beginning of all programs without later redefinitions. The syntax lets you use the %VAR IS COMMON clause without duplicating the previous attributes.
However, if attributes, such as STRING or LEN, are included in the redefinition, which differ from those previously defined, a compilation error occurs. In addition, if the variable is not previously defined, it is allocated based on the current default variable definition.
For example,
BEGIN %X IS STRING LEN 3 INITIAL ('AAA') STATIC COMMON SUBROUTINE A * full definition on next line no longer required ** %X IS STRING LEN 3 INITIAL ('AAA') STATIC COMMON * new syntax on next line replaces previous line %X IS COMMON /? defaults to previous %X definition ?/ CALL B END SUBROUTINE SUBROUTINE B * But next line will fail compile since %X already exists %X IS STRING LEN 4 COMMON /? compiler error ?/ * And the next line will result in a default definition * since %Y is not previously defined. %Y IS COMMON END SUBROUTINE PRINT %X CALL A END
The following examples illustrate the differences between data that is locally scoped and data that is shared using the COMMON keyword.
Example 1
In this example, the variable %I assumes different values depending upon which part of the request is being executed:
BEGIN %I IS FIXED DECLARE SUBROUTINE SUBR1(STRING) . . . FOR %I FROM 1 TO 10 CALL SUBR1(%ARR(%I)) END FOR . . . SUBROUTINE SUBR1(%A IS STRING) %I IS FIXED FOR %I FROM 1 TO 10 PRINT %I and %A END FOR END SUBROUTINE END
Example 2
In this example, the screen EXAMPLE and the %A and %B variables retain the same values no matter which part of the request is being executed:
BEGIN SCREEN EXAMDEF COMMON TITLE 'THIS IS A COMMON SCREEN DEFINITION' PROMPT 'ENTER VALUE' INPUT FLD1 AT 10 LEN 20 PAD '_' END SCREEN %A IS STRING LEN 10 COMMON %B IS FLOAT COMMON DECLARE SUBROUTINE SUBR1(STRING LEN 10) . . . SUBROUTINE SUBR1(%Z IS STRING LEN 10) DECLARE SCREEN EXAMDEF COMMON %A IS STRING LEN 10 COMMON %B IS FLOAT COMMON . . . END
Example 3
In this example, the main request and the subroutine share the common data of an array and a found set:
BEGIN %COM.ARRAY IS STRING LEN 10 ARRAY(10,10) COMMON DECLARE LABEL ALL.RECS COMMON %I IS FIXED DECLARE SUBROUTINE EXAMPLE . . . ALL.RECS: FIND ALL RECORDS END FIND %I = 0 %J = 1 FOR 10 RECORDS IN ALL.RECS %I = %I + 1 %COM.ARRAY(%I,%J) = FLD END FOR CALL EXAMPLE STOP SUBROUTINE EXAMPLE %COM.ARRAY IS STRING LEN 10 ARRAY(10,10) COMMON DECLARE LABEL ALL.RECS COMMON %I IS FIXED %I = 0 %J = 2 FOR 10 RECORDS IN ALL.RECS %I = %I + 1 %COM.ARRAY(%I,%J) = FLD2 END FOR END SUBROUTINE END
In the preceding example, the main request updates column 1 of the array %COMM.ARRAY with the contents of FLD. The subroutine, EXAMPLE, updates column 2 of the same array with the contents of FLD2. Both the array %COM.ARRAY and the found set ALL.RECS are shared by using the COMMON keyword. %I and %J are both local variables. %I is local because it was declared without the COMMON keyword; %J is local because it was not declared at all.
On units
An On unit lets you specify a course of action following a triggering event, such as the terminal operator pressing one of the Attention identifier (AID) keys. The On unit provides an application with a way to override the normal system response.
Syntax
To define an On unit, use an On block in the following format:
[label] On unittype statement ... End On
where unittype is one of the following:
unittype | You can specify the course of action to take if... |
---|---|
ATTENTION | The end user invokes the attention feature (for example, presses the BREAK, ATTN, or PA1 key, or enters *CANCEL). |
ERROR | Model 204 cancels a request. Before a request is canceled or, for transaction backout files, after the current transaction is backed out, the ON ERROR unit is processed instead of returning control to the terminal command level. For more information on transaction backout files, see Data recovery. |
FIELD CONSTRAINT CONFLICT (FCC) | There are field level constraint conflicts. Violating the UNIQUE and AT-MOST-ONE attributes causes field-level conflicts.* |
FIND CONFLICT | A conflict arises evaluating a FIND statement or a FOR EACH RECORD statement used for retrieval. |
MISSING FILE | A remote file is no longer available. |
MISSING MEMBER | A remote optional member is no longer available. |
RECORD LOCKING CONFLICT | A conflict arises during a record locking attempt. |
- If you have procedures written for Model 204 V2R1.0 that use ON FCC for UNIQUE fields, and you are planning to use ON FCC for AT-MOST-ONE fields, you might need to rewrite the ON FCC unit to take the new value of $UPDSTAT (2 for AT-MOST-ONE) into account.
Several $functions provide information about conflicts. They are:
$UpdFile | $UpdFld | $UpdOval | $UpdRec |
$UpdStat | $UpdStmt | $UpdVal | $UnqRec |
Body of an ON unit
The body of the ON unit consists of statements immediately following the ON statement. Any User Language statement can appear within an ON unit except the SUBROUTINE statement.
Ending an ON unit
You must conclude an ON unit with either an END ON statement or an END BLOCK statement. The format of the END ON statement is as follows:
END ON [label]
where label is the label of the statement that began the ON statement.
Processing an ON unit
When an ON statement is evaluated, Model 204 remembers the location of the ON unit body (the statement after the ON statement and within the unit), but does not immediately evaluate the body. Instead, it passes control to the statement immediately following the ON unit. The ON unit body is evaluated only when the triggering event takes place.
Usage guidelines for ON units
Note the following considerations when using ON units:
- You must define the ON unit before it is invoked (that is, you should typically place the ON unit near the beginning of your procedure).
- A subsequent definition of an ON unit of the same kind replaces the previous one. For example, if you define two ON ATTN units, the second one becomes the current one.
- You can jump to destinations within the same ON unit.
- You can jump out of an ON unit only to unnested, labeled statements.
- You cannot jump into an ON unit.
- You can jump outside of an ON unit only if the ON unit is part of a main routine or part of a complex subroutine.
- You cannot jump out of an ON unit contained in a simple subroutine, regardless of whether the destination is within the subroutine or back in the mainline program, because ON units are part of the mainline program; hence, there is no scoping of ON units. This restriction ensures that the ON unit is executed and the control returned to the main routine.
- An ON unit definition is not preserved when a request is continued with an END MORE statement and a MORE command. Each new request continuation must define its own ON units. (See Rules for request continuation.)
- For complex subroutines, an active ON unit is temporarily disabled when a subroutine is called that contains an ON UNIT of the same type; it is restored when the subroutine returns. Any ON unit enabled during the execution of a subroutine is replaced by the active ON unit at the time of the last CALL statement as soon as the RETURN statement is evaluated.
- ON units coded inside complex subroutines can contain JUMP statements that specify a destination outside the ON unit. The destination must be to an unnested labeled statement within the complex subroutine. Also, if the ON unit is to be jumped out of, the condition that causes the ON unit to be invoked must be raised in the subroutine that contains the ON unit.
This prevents a lower level subroutine from returning inadvertently by raising a condition for which there is an ON unit coded in a higher level subroutine. If the inadvertent return is attempted, the request is canceled.
- ON units do not have their own local data and labels. They inherit the scope of the part of the program in which they are compiled. An ON unit that is compiled within a complex subroutine is considered part of only that subroutine.
Example
In the following example, if the user presses the ATTN key instead of entering a response to the prompt in the GET.REC.TYPE statement, the ON ATTENTION unit sets %FLAG to 1. The FLAG.SET statement tests %FLAG. If %FLAG is set, the request branches to END.REQUEST and the FIND statement is not executed.
%FLAG=0 ON ATTENTION %FLAG=1 END ON GET.REC.TYPE: %TYPE=$READ('ENTER RECORD TYPE') FLAG.SET: IF %FLAG=1 THEN JUMP TO END.REQUEST END IF FIND.RECS: FIND ALL RECORDS FOR WHICH TYPE = %TYPE END FIND . . . END.REQUEST: END
Only one ON unit at a time
Only one ON unit of each kind is active at a time. For example, the processing of a second ON ERROR statement resets the current ON ERROR unit, but does not affect the current ON ATTENTION unit. Thus, you can redefine what to do in a variety of cases by having several ON statements and units within a request. An ON unit can be redefined within another ON unit.
Passing control to and from ON units
Model 204 passes control to an ON unit when the triggering event occurs. For example, an ON ATTENTION unit receives control when a user presses one of the ATTENTION identifier (AID) keys at the terminal during the execution of a request. Use one of the following statements to return control to the body of the request.
BYPASS statement
The BYPASS statement handles the various unittypes of an ON statement as follows:
For unittype... | The BYPASS statement... |
---|---|
ON MISSING FILE ON MISSING MEMBER |
Returns control to the statement immediately after the END FOR statement that closes the current FOR loop. |
ON ERROR | Ends the request. |
ON ATTENTION ON FIND CONFLICT |
Returns control to the statement immediately after the statement that invoked the ON unit. |
The format of the BYPASS statement is as follows:
BYPASS [PENDING STATEMENT]
where the PENDING STATEMENT keyword is optional. If the ON unit is not ended with a BYPASS statement, Model 204 automatically generates a STOP statement at the end of the unit.
CONTINUE statement
The CONTINUE statement is supported only with the Parallel Query Option. If you lose access to a group member that is an optional file during FOR processing, the CONTINUE statement continues FOR processing with the next available file, and skips any other unavailable files.
JUMP TO statement
The JUMP statement, when used to jump to a labeled statement outside the ON unit, causes the request to continue at that point. There are restrictions pertaining to jumps; see Branching statements and Simple subroutines.
RETRY statement
The RETRY statement passes control to the statement that invoked the ON unit, thereby retrying that statement. The RETRY statement is not valid in an ON ERROR unit.
The format of the RETRY statement is as follows:
RETRY [PENDING STATEMENT]
where the PENDING STATEMENT keyword is optional.
STOP statement
The STOP statement is used to end the request.
Clearing ON units
You can issue the following statement to clear the definition of an ON unit:
CLEAR ON unittype
This statement clears any defined ON unit of the type specified. Clearing an ON unit produces the following results:
- After a CLEAR ON ATTENTION, pressing one of the ATTENTION identifier (AID) keys at the terminal does not invoke the ON ATTENTION unit.
- After a CLEAR ON ERROR, a request cancellation error does not invoke the ON ERROR unit.
An ON unit can be defined, cleared, and then redefined.
Pausing during the request
The PAUSE statement can be used with ON units to cause the request to wait a specified time and then to retry the statement that caused the evaluation of the ON unit. PAUSE is typically used with the other ON unit types (RECORD LOCKING CONFLICT and ON FIND CONFLICT) and is discussed in Record level locking and concurrency control.