Subroutines: Difference between revisions
mNo edit summary |
m (add links) |
||
(26 intermediate revisions by 7 users not shown) | |||
Line 1: | Line 1: | ||
===Overview | <div class="toclimit-3"> | ||
<p>User Language lets you treat a single set of statements as a simple or complex subroutine. You can:</p> | |||
==Overview== | |||
<p> | |||
User Language lets you treat a single set of statements as a simple or complex subroutine. You can:</p> | |||
<ul> | <ul> | ||
<li>Execute simple subroutines a number of times from different locations within a request.</li> | <li>Execute simple subroutines a number of times from different locations within a request.</li> | ||
<li>Use complex subroutines as you would simple subroutines. | <li>Use complex subroutines as you would simple subroutines. </li> | ||
</ul> | </ul> | ||
< | In addition, you can: | ||
< | <ul> | ||
<li>Pass parameters via parameter lists.</li> | |||
<p>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.</p> | <li>Declare variables locally.</li> | ||
=== | </ul> | ||
<p> | |||
===Common elements=== | |||
<p> | |||
<p>The following statements are used in simple subroutines within a request:</p> | 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 <var>COMMON</var>.</p> | ||
===On units=== | |||
<p> | |||
As [[#On statement|described later]], an <var>On</var> unit specifies a response action to a triggering event: for example, the terminal operator pressing one of the ATTENTION identifier (AID) keys. <var>On</var> units let an application override the normal system response.</p> | |||
==Simple subroutines== | |||
===Outlining a simple subroutine=== | |||
<p> | |||
The following statements are used in simple subroutines within a request:</p> | |||
<table> | <table> | ||
<tr> | <tr class="head"> | ||
< | <th>Statement</th> | ||
< | <th>Purpose</th></tr> | ||
</tr> | |||
<tr> | <tr> | ||
<td><var>CALL</var></td> | <td><var>CALL</var></td> | ||
<td>Transfers control to the subroutine.</td> | <td>Transfers control to the subroutine.</td></tr> | ||
</tr> | |||
<tr> | <tr> | ||
<td>END SUBROUTINE</td> | <td nowrap><var>END SUBROUTINE</var></td> | ||
<td>Indicates the end of the subroutine.</td> | <td>Indicates the end of the subroutine.</td></tr> | ||
</tr> | |||
<tr> | <tr> | ||
<td>JUMP TO</td> | <td><var>JUMP TO</var></td> | ||
<td>Transfers control to another statement in the request.</td> | <td>Transfers control to another statement in the request.</td></tr> | ||
</tr> | |||
<tr> | <tr> | ||
<td><var>RETURN</var></td> | <td><var>RETURN</var></td> | ||
<td>Returns control to the statement immediately following the CALL statement. </td> | <td>Returns control to the statement immediately following the CALL statement. </td></tr> | ||
</tr> | |||
<tr> | <tr> | ||
<td><var>SUBROUTINE</var></td> | <td><var>SUBROUTINE</var></td> | ||
<td>Indicates the beginning of a subroutine. </td> | <td>Indicates the beginning of a subroutine. </td></tr> | ||
</tr> | |||
</table> | </table> | ||
<p>The statements are coded in the following sequence, so they are described in order of usage.</p> | <p> | ||
The statements are coded in the following sequence, so they are described in order of usage.</p> | |||
<ol> | <ol> | ||
<li>SUBROUTINE statement</li> | <li><var>SUBROUTINE</var> statement</li> | ||
<li>JUMP TO and RETURN statements as appropriate</li> | <li><var>JUMP TO</var> and <var>RETURN</var> statements as appropriate</li> | ||
<li>END SUBROUTINE statement</li> | <li><var>END SUBROUTINE</var> statement</li> | ||
<li>CALL statement</li> | <li><var>CALL</var> statement</li> | ||
</ol> | </ol> | ||
<p>A simple subroutine consists of a sequence of | ===SUBROUTINE statement=== | ||
<p> | |||
<p>The format of the SUBROUTINE statement is: </p> | A simple subroutine consists of a sequence of SOUL statements that must begin with a <var>SUBROUTINE</var> statement. The body of the subroutine is composed of the statements immediately following the <var>SUBROUTINE</var> statement. </p> | ||
<p class=" | |||
====Syntax==== | |||
<p> | |||
The format of the <var>SUBROUTINE</var> statement is: </p> | |||
<p class="syntax"><span class="term">label</span>: SUBROUTINE | |||
</p> | </p> | ||
<p>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.</p> | <p> | ||
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.</p> | |||
<p>The following request uses two subroutines. </p> | |||
====Example of a simple subroutine==== | |||
<p> | |||
The following request uses two subroutines. </p> | |||
<p class="note"><b>Note:</b> Simple subroutine statements in the following example are described more fully after this example.</p> | <p class="note"><b>Note:</b> Simple subroutine statements in the following example are described more fully after this example.</p> | ||
<p>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. | <p> | ||
The first subroutine, <code>TOTAL</code>, accumulates a total in the %variable named <code>%TOTAL.MUSTANGS</code>. The second subroutine, <code>PRINT.TOTAL</code>, prints a literal that varies depending on the total accumulated. </p> | |||
<p class="code">BEGIN | <p class="code">BEGIN | ||
MUSTANGS: FIND ALL RECORDS FOR WHICH | MUSTANGS: FIND ALL RECORDS FOR WHICH | ||
Line 83: | Line 104: | ||
RETURN | RETURN | ||
END SUBROUTINE PRINT.TOTAL | END SUBROUTINE PRINT.TOTAL | ||
END | END | ||
</p> | </p> | ||
<p><var class="product">Model 204</var> compiles subroutines only after they are called, regardless of where they are in your program.</p> | ===JUMP TO statements=== | ||
<p>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.</p> | <p> | ||
<var class="product">Model 204</var> compiles subroutines only after they are called, regardless of where they are in your program.</p> | |||
<p>The RETURN statement returns control from the subroutine to the statement following the most recent CALL statement. </p> | <p> | ||
If you use a <var>JUMP TO</var> statement, its destination must be within the same subroutine. You cannot jump into a subroutine from outside of a subroutine.</p> | |||
<p>The format of the RETURN statement is: | |||
<p class=" | ===RETURN statement=== | ||
<p> | |||
The <var>RETURN</var> statement returns control from the subroutine to the statement following the most recent <var>CALL</var> statement. </p> | |||
====Syntax==== | |||
<p> | |||
The format of the <var>RETURN</var> statement is: </p> | |||
<p class="syntax">RETURN | |||
</p> | </p> | ||
<p>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: </p> | <p> | ||
<p class="code">M204.1779 RETURN IS INVALID IN ON UNITS, USE BYPASS STATEMENT | The <var>RETURN</var> statement can appear only within the body of a subroutine. Use of the <var>RETURN</var> statement inside of an <var>ON</var> unit terminates compilation, the procedure cannot run, and the following message is produced: </p> | ||
<p class="code">M204.1779 RETURN IS INVALID IN ON UNITS, USE BYPASS STATEMENT | |||
</p> | </p> | ||
<p><var class="product">Model 204</var> automatically generates a return at the end of a subroutine, if you do not specify one. | <p> | ||
<p>For more information see [[#ON units|ON units]] and [[#Passing control to and from ON units|Passing control to and from ON units]].</p> | <var class="product">Model 204</var> automatically generates a return at the end of a subroutine, if you do not specify one. </p> | ||
<p> | |||
<p>You can end a subroutine using an END SUBROUTINE statement or an END BLOCK statement. </p> | For more information, see [[#ON units|ON units]] and [[#Passing control to and from ON units|Passing control to and from ON units]].</p> | ||
<p>The END SUBROUTINE statement is formatted as follows: </p> | ===END SUBROUTINE statement=== | ||
<p class=" | <p> | ||
You can end a subroutine using an <var>END SUBROUTINE</var> statement or an <var>END BLOCK</var> statement. </p> | |||
====Syntax==== | |||
<p> | |||
The END SUBROUTINE statement is formatted as follows: </p> | |||
<p class="syntax">END SUBROUTINE <span class="squareb">[</span><span class="term">label</span><span class="squareb">]</span> | |||
</p> | </p> | ||
<p>The CALL statement transfers control to the subroutine. </p> | ===CALL statement=== | ||
<p> | |||
<p>The format of the CALL statement is:</p> | The <var>CALL</var> statement transfers control to the subroutine. </p> | ||
<p class=" | |||
====Syntax==== | |||
<p> | |||
The format of the <var>CALL</var> statement is:</p> | |||
<p class="syntax">CALL <span class="term">label</span> | |||
</p> | </p> | ||
<p>This statement:</p> | ====Example==== | ||
<p class="code">CALL.SUB: CALL COMPUTE.PREMIUM | <p> | ||
This statement:</p> | |||
<p class="code">CALL.SUB: CALL COMPUTE.PREMIUM | |||
</p> | </p> | ||
<p>transfers control to the following statement:</p> | <p> | ||
transfers control to the following statement:</p> | |||
<p class="code">COMPUTE.PREMIUM: SUBROUTINE | <p class="code">COMPUTE.PREMIUM: SUBROUTINE | ||
</p> | </p> | ||
<p>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.</p> | <p> | ||
<p>Processing continues sequentially from that statement until another control transfer statement | 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 <var>FOR EACH RECORD</var> loop on the set of records to be processed.</p> | ||
<p> | |||
<p>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. </p> | Processing continues sequentially from that statement until another control transfer statement — a <var>CALL</var>, <var>IF</var>, <var>JUMP TO</var>, <var>RETURN</var>, or <var>STOP</var> statement — is encountered. </p> | ||
<p class="note"><b>Note:</b> The format of the SUBROUTINE statement changes for a complex subroutine and the CALL statement is also more complex.</p> | |||
==Complex subroutines== | |||
<p>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:</p> | <p> | ||
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. </p> | |||
<p class="note"><b>Note:</b> The format of the <var>SUBROUTINE</var> statement changes for a complex subroutine and the <var>CALL</var> statement is also more complex.</p> | |||
===Symbolic parameter passing=== | |||
<p> | |||
You can specify positional parameters on the <var>CALL</var> 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:</p> | |||
<ul> | <ul> | ||
<li>Scalar %variables</li> | <li>Scalar %variables</li> | ||
<li>%variable arrays</li> | <li>%variable arrays</li> | ||
<li>Lists of records</li> | <li>Lists of records</li> | ||
</ul> | </ul> | ||
<p>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.</p> | ===Local variable declaration=== | ||
<p> | |||
<p>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.</p> | 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.</p> | ||
< | ===Processing=== | ||
<p class="code">M204.1521: entity-name DOES NOT EXIST OR REQUESTED ACCESS NOT AUTHORIZED | <p> | ||
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.</p> | |||
====OPEN statement within a complex subroutine==== | |||
<p> | |||
A SOUL <var>OPEN</var> statement — an <var>OPEN</var> statement inside a <var>BEGIN/END</var> — 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:</p> | |||
<p class="code">M204.1521: <i>entity-name</i> DOES NOT EXIST OR REQUESTED ACCESS NOT AUTHORIZED | |||
</p> | |||
===SUBROUTINE statement for complex subroutines=== | |||
<p> | |||
Complex subroutines begin with the following form of the <var>SUBROUTINE</var> statement: </p> | |||
<p class="syntax">SUBROUTINE <span class="term">subname</span> <span class="squareb">[</span>(<span class="term">formal-parameter</span> | |||
<span class="squareb">[</span><span class="term">inout-option</span><span class="squareb">]</span> <span class="squareb">[</span>, ...<span class="squareb">]</span>)<span class="squareb">]</span> | |||
</p> | </p> | ||
Where: | |||
< | <ul> | ||
< | <li><var class="term">subname</var> is the name of the subroutine.</li> | ||
<var> | <li><var class="term">formal-parameter</var> is the declaration of a symbolic parameter used within the subroutine. The parameter list is comprised of the <var class="term">formal-parameter</var> 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.</li> | ||
< | <li><var class="term">formal-parameter</var> specifies the name and type of element to be used within the subroutine and can be any of the following: | ||
<ul> | <ul> | ||
<li>Scalar (nonarray) %variable formatted as:< | <li>Scalar (nonarray) %variable formatted as: | ||
< | <p class="syntax"><span class="term">%variable</span> [IS] {[STRING] [LEN] <span class="term">n</span> [DP {<span class="term">n</span> | *}] | ||
<span class="squareb">|</span> [FIXED [DP <span class="term">n</span>] <span class="squareb">|</span> FLOAT]} | |||
</p> | </p> | ||
<p>For example:</p> | <p> | ||
For example:</p> | |||
<p class="code">SUBROUTINE EXAMPLE (%W IS STRING DP * - | <p class="code">SUBROUTINE EXAMPLE (%W IS STRING DP * - | ||
%X IS STRING LEN 40, - | %X IS STRING LEN 40, - | ||
%Y IS STRING LEN 10 DP 2, - | %Y IS STRING LEN 10 DP 2, - | ||
%Z IS FLOAT, - | %Z IS FLOAT, - | ||
%A IS FIXED DP 4) | %A IS FIXED DP 4) | ||
</p></li> | </p></li> | ||
|{FIXED [DP n] | FLOAT} [ARRAY(* [,*[,*]])]] | <li>Array %variable formatted as: | ||
<p class="syntax"><span class="term">%variable</span> [IS] [[STRING] [LEN <span class="term">n</span>] [DP {<span class="term">n</span> | *}] | |||
[ARRAY(* [,*[,*]])] [NO FIELD SAVE] | |||
<span class="squareb">|</span> <span class="squareb">{</span>FIXED [DP <span class="term">n</span>] <span class="squareb">|</span> FLOAT<span class="squareb">}</span> [ARRAY(* [,*[,*]])]] | |||
</p> | </p> | ||
<p>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:</p> | <p> | ||
<p class="code">SUBROUTINE EXAMPLE(%ARR IS STRING LEN 10 - ARRAY(*,*)) | The number of dimensions must be specified in the subroutine declaration, but the exact size of each dimension is not specified. An asterisk (<tt>*</tt>) is used instead of the dimension size. For example:</p> | ||
<p class="code">SUBROUTINE EXAMPLE(%ARR IS STRING LEN 10 - ARRAY(*,*)) | |||
</p></li> | </p></li> | ||
name | <li>A list of records formatted as: | ||
<p class="syntax">[LIST] <span class="term">listname</span> [IN [FILE | [PERM | TEMP] GROUP]] <span class="term">name</span> | |||
</p> | </p> | ||
<p>The context of the list is restricted to a single file or group. For example:</p> | <p> | ||
The context of the list is restricted to a single file or group. For example:</p> | |||
<p class="code">SUBROUTINE GENERAL(LIST TOTRECS IN FILE VEHICLES) | <p class="code">SUBROUTINE GENERAL(LIST TOTRECS IN FILE VEHICLES) | ||
</p></li> | </p></li> | ||
</ul> | </ul> | ||
< | |||
<li><var class="term">inout-option</var> 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: | |||
<table> | <table> | ||
<tr> | <tr class="head"> | ||
< | <th>Option</th> | ||
< | <th>Data can be...</th> | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td>INPUT | <td>INPUT<br> | ||
(the default)</td> | (the default)</td> | ||
<td>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.</td> | <td>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 <var>INPUT</var> parameters that update the parameter, for example, assignment statements, result in compiler errors.</td> | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td>INPUT OUTPUT</td> | <td nowrap>INPUT OUTPUT</td> | ||
<td>This option is equivalent to the OUTPUT option. </td> | <td>This option is equivalent to the OUTPUT option. </td> | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td>OUTPUT</td> | <td>OUTPUT</td> | ||
<td | <td>Returned from a subroutine. No data conversions are performed. | ||
<p> | |||
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: </p> | |||
<p class="code">M204.1981 LENGTH IGNORED FOR OUTPUT PARAMETER | <p class="code">M204.1981 LENGTH IGNORED FOR OUTPUT PARAMETER | ||
</p> | </p> | ||
<p>Compilation and evaluation of the procedure continues, with the inherited length.</p> | <p> | ||
Compilation and evaluation of the procedure continues, with the inherited length.</p> | |||
</td> | </td> | ||
</tr> | </tr> | ||
</table> | </table></li> | ||
</ul> | |||
<p>Like simple subroutines, a complex subroutine ends with an END SUBROUTINE statement. However, unlike simple subroutines, you must not label the SUBROUTINE statement. </p> | |||
<p>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. | ===END SUBROUTINE statement=== | ||
<p> | |||
<p>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.</p> | Like simple subroutines, a complex subroutine ends with an <var>END SUBROUTINE</var> statement. However, unlike simple subroutines, you must not label the <var>SUBROUTINE</var> statement. </p> | ||
<p> | |||
<p>A complex subroutine is invoked with this form of the CALL statement:</p> | All declarations and statements compiled after the <var>SUBROUTINE</var> statement become a part of the subroutine until the <var>END SUBROUTINE</var> statement is encountered or until the request is ended with the <var>END</var> statement. </p> | ||
<p class=" | |||
===CALL statement=== | |||
<p> | |||
The execution of a <var>CALL</var> 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.</p> | |||
====Syntax==== | |||
<p> | |||
A complex subroutine is invoked with this form of the <var>CALL</var> statement:</p> | |||
<p class="syntax">CALL <span class="term">subname</span> [(<span class="term">actual-parameter</span> [, ...])] | |||
</p> | </p> | ||
Where: | |||
<ul> | <ul> | ||
<li>%variable or expression | <li><var class="term">subname</var> is the name of the subroutine.</li> | ||
<p>Expressions can contain field names, screen items, image items, and constants.</p> | |||
</li> | <li><var class="term">actual-parameter</var> 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 <var>SUBROUTINE</var> statement to an actual-parameter in the <var>CALL</var> statement is by position rather than by name. | ||
<li>Array %variable | <p> | ||
<p>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:</p> | An actual-parameter can be any of the following:</p> | ||
<ul> | |||
<li>%variable or expression | |||
<p> | |||
Expressions can contain field names, screen items, image items, and constants.</p></li> | |||
<li>Array %variable | |||
<p> | |||
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:</p> | |||
<p class="code">BEGIN | <p class="code">BEGIN | ||
%A IS FLOAT ARRAY(10,10) | %A IS FLOAT ARRAY(10,10) | ||
Line 243: | Line 316: | ||
PRINT %B(1,1) | PRINT %B(1,1) | ||
END SUBROUTINE | END SUBROUTINE | ||
END | END | ||
</p> | </p> | ||
<p>The $ | <p> | ||
</li> | The <var>[[$ArrSize]]</var> function can be used to determine the number of elements in a particular dimension of an array. </p></li> | ||
<li>A list | |||
<p>An actual-parameter can be a list, optionally preceded by the word LIST to distinguish it from a field name. </p> | <li>A list | ||
< | <p> | ||
An actual-parameter can be a list, optionally preceded by the word LIST to distinguish it from a field name. </p> | |||
<p> | |||
<p>If the list has never been declared in the request, a compilation error results. </p> | If the compiler already knows of the formal-parameter list because the subroutine is already compiled or declared, the LIST keyword is unnecessary. </p> | ||
< | <p> | ||
<p>The following additional rules apply to actual-parameters and parameter passing:</p> | If the list has never been declared in the request, a compilation error results. </p> | ||
</li></ul> | |||
</li></ul> | |||
====Additional rules for parameters==== | |||
<p> | |||
The following additional rules apply to actual-parameters and parameter passing:</p> | |||
<ul> | <ul> | ||
<li>The maximum number of parameters that can be passed in complex subroutines is 63.</li> | <li>The maximum number of parameters that can be passed in complex subroutines is 63.</li> | ||
<li>If the formal-parameter is specified as an OUTPUT parameter, the corresponding actual-parameter must match type.</li> | <li>If the formal-parameter is specified as an OUTPUT parameter, the corresponding actual-parameter must match type.</li> | ||
<li>The INPUT parameters of a %variable array requires that the type of the array — <var>FLOAT</var>, <var>FIXED</var>, or <var>STRING</var> — the <var>DP</var> specification, and the number of dimensions match the actual-parameter. In addition, if the <var>NO FIELD SAVE</var> (<var>NO FS</var>) option is specified for a <var>STRING</var> 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.</li> | |||
<li>The INPUT parameters of a list requires the same file or group context as the actual-parameter. | |||
</li> | </li> | ||
<li>An INPUT parameter of a scalar %variable can be called with an actual-parameter that is an expression or %variable of a differing type. <var class="product">Model 204</var> 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. | |||
<p> | |||
Passing arguments to INPUT parameters might involve truncation where the number of decimal places is different, or conversion to 0 for non-numeric strings.</p> | |||
<li>An INPUT parameter of a scalar %variable can be called with an actual-parameter that is an expression or %variable of a differing type. <var class="product">Model 204</var> 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. | |||
<p>Passing arguments to INPUT parameters might involve truncation where the number of decimal places is different, or conversion to 0 for non-numeric strings.</p> | |||
</li> | </li> | ||
<li>OUTPUT parameters of lists and %variable arrays follow the same rules as INPUT parameters for lists and %variable arrays.</li> | <li>OUTPUT parameters of lists and %variable arrays follow the same rules as INPUT parameters for lists and %variable arrays.</li> | ||
</li> | </li> | ||
<li>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 | |||
<p class="code">M204.1725 PARAMETER NUMBER n IS TYPE INCOMPATIBLE | <li>OUTPUT parameters of scalar %variables require that the actual-parameter supplied in the <var>CALL</var> statement be another scalar %variable (no constants or expressions) of the same type — <var>FLOAT</var>, <var>FIXED</var>, or <var>STRING</var> — and that the number of decimal places be the same, or the following message is issued by <var class="product">Model 204</var>: | ||
<p class="code">M204.1725 PARAMETER NUMBER <i>n</i> IS TYPE INCOMPATIBLE | |||
</p> | </p> | ||
<p>Strings can be of any length. The length used is that of the actual input parameter. </p> | <p> | ||
Strings can be of any length. The length used is that of the actual input parameter. </p> | |||
</li> | </li> | ||
</ul> | </ul> | ||
<p>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.</p> | ===Processing complex subroutines=== | ||
<p>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. </p> | <p> | ||
<p>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. </p> | 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.</p> | ||
<p> | |||
<p>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:< | 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. </p> | ||
< | <p> | ||
< | 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. </p> | ||
</ | ===DECLARE SUBROUTINE statement=== | ||
</p> | <p> | ||
The DECLARE statement for a complex subroutine is similar to the SUBROUTINE statement, except that the parameter names are omitted. </p> | |||
====Syntax==== | |||
The complete syntax for the DECLARE SUBROUTINE statement is: | |||
<p class="syntax">DECLARE SUBROUTINE <span class="term">subname</span> <span class="squareb">[</span>(<span class="term">type</span> <span class="squareb">[</span><span class="term">inout-option</span><span class="squareb">]</span> <span class="squareb">[</span>, ...<span class="squareb">]</span>)<span class="squareb">]</span> </p> | |||
Where: | |||
<ul> | <ul> | ||
<li> | <li><var class="term">subname</var> is the name of the subroutine.</li> | ||
| FLOAT]} | <li><var class="term">type</var> is one of the following: | ||
<ul> | |||
<li>Scalar %variable of the following format: | |||
<p class="syntax">{STRING [LEN] <span class="term">n</span> [DP {<span class="term">n</span> | *}] | [FIXED [DP <span class="term">n</span>] | FLOAT]} | |||
</p></li> | </p></li> | ||
<li>Array %variable of the following format: | |||
<p class="syntax">{STRING [LEN <span class="term">n</span>] [DP [<span class="term">n</span> | *}] [ARRAY(* [,*[,*]]) [NO FIELD SAVE]] | |||
| [FIXED [DP <span class="term">n</span>] | FLOAT] [ARRAY(* [,*[,*]])]} | |||
</p></li> | |||
<li>A list of records of the following format: | |||
<p class="code">[LIST] [IN {FILE | [PERM | TEMP] GROUP} <span class="term">name</span>] | |||
<li>A list of records of the following format: | |||
<p class="code">[LIST] [IN {FILE | [PERM | TEMP] GROUP} name] | |||
</p></li> | </p></li> | ||
</ul></li> | |||
<li> | |||
<var class="term">inout-option</var> 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. </li> | |||
</ul> | </ul> | ||
====DECLARE before CALL==== | |||
< | When a <var>CALL</var> 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 <var>CALL</var> statement for a particular subroutine is not correctly coded, then a large number of error messages can be generated when compiling subsequent <var>CALL</var> statements, although these statements might be correctly coded. | ||
You can avoid this problem in one of the following ways: | |||
<ul> | <ul> | ||
<li>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. </li> | <li>Declare the formal parameter list with the <var>DECLARE</var> statement. When the <var>DECLARE</var> statement precedes the first <var>CALL</var>, the compiler generates the correct error messages, if problems are encountered. </li> | ||
<li>Place the subroutine before the first CALL | <li>Place the subroutine before the first <var>CALL</var> statement.</li> | ||
</li> | |||
</ul> | </ul> | ||
<p>If you specify a LIST (of records), you must also specify FILE or GROUP. For example:</p> | ====Specifying a list of records==== | ||
<p> | |||
If you specify a <var>LIST</var> (of records), you must also specify <var>FILE</var> or <var>GROUP</var>. For example:</p> | |||
<p class="code">OPEN METADATA | <p class="code">OPEN METADATA | ||
password | password | ||
BEGIN | BEGIN | ||
DECLARE LIST ORIG IN METADATA | DECLARE LIST ORIG IN METADATA | ||
Line 335: | Line 429: | ||
END | END | ||
</p> | </p> | ||
<p>If you do not specify FILE or GROUP in the code, you receive the following error message:</p> | <p> | ||
If you do not specify <var>FILE</var> or <var>GROUP</var> in the code, you receive the following error message:</p> | |||
<p class="code">M204.1725: PARAMETER NUMBER n IS TYPE INCOMPATIBLE | <p class="code">M204.1725: PARAMETER NUMBER n IS TYPE INCOMPATIBLE | ||
</p> | </p> | ||
<p>In the following example, note that the DECLARE statement lists the formal parameters, while the CALL statement lists the actual parameters:</p> | ====DECLARE parameters and CALL parameters==== | ||
<p class="code">DECLARE SUBROUTINE SUB1(FLOAT, FIXED DP 2, STRING, | <p> | ||
In the following example, note that the <var>DECLARE</var> statement lists the formal parameters, while the <var>CALL</var> statement lists the actual parameters:</p> | |||
<p class="code">DECLARE SUBROUTINE SUB1(FLOAT, FIXED DP 2, STRING, STRING DP *) | |||
. | . | ||
. | . | ||
Line 354: | Line 450: | ||
. | . | ||
. | . | ||
END | END | ||
</p> | </p> | ||
< | ====Impact on CALL and SUBROUTINE statements==== | ||
<p>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.</p> | <p> | ||
The <var>CALL</var> and <var>SUBROUTINE</var> statements are not overridden when a <var>DECLARE SUBROUTINE</var> statement is present. The <var>DECLARE SUBROUTINE</var> statement does not cause the compiler to use more table space, nor does it alter the way in which the compiler output is generated.</p> | |||
<p>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.</p> | <p> | ||
<p>If a JUMP statement is used within a subroutine, its destination must be within the same subroutine. You cannot jump into a subroutine. | Rocket Software recommends that you use a <var>DECLARE SUBROUTINE</var> statement before the <var>CALL</var> statement for a subroutine or that you place the subroutine itself before the <var>CALL</var> statement. Error reporting will be more complete and specific to the subroutine.</p> | ||
<p>A STOP statement, when executed inside a subroutine, terminates the request in the same manner as it does when executed outside a subroutine. | |||
===Exiting the subroutine=== | |||
<p> | <p> | ||
The <var>RETURN</var> statement returns control to the statement immediately following the most recent <var>CALL</var> statement. If an <var>END SUBROUTINE</var> or <var>END</var> statement is encountered without a <var>RETURN</var> statement, <var>RETURN</var> processing is implied and automatically added by the compiler. More than one <var>RETURN</var> statement can be specified in a subroutine.</p> | |||
<p> | |||
If a <var>JUMP</var> statement is used within a subroutine, its destination must be within the same subroutine. You cannot jump into a subroutine. </p> | |||
<p> | |||
A <var>STOP</var> statement, when executed inside a subroutine, terminates the request in the same manner as it does when executed outside a subroutine. </p> | |||
===Examples using complex subroutines=== | |||
<p> | |||
In the following example, an employee name with regular and overtime hours is passed to the subroutine <code>CALC.WAGES</code>. The total pay is returned and a list of records processed by the routine is updated.</p> | |||
<p class="code">BEGIN | <p class="code">BEGIN | ||
DECLARE SUBROUTINE CALC.WAGES(FIXED DP 2, - | DECLARE SUBROUTINE CALC.WAGES(FIXED DP 2, - | ||
Line 382: | Line 487: | ||
%TOTAL IS FIXED DP 2 OUTPUT, - | %TOTAL IS FIXED DP 2 OUTPUT, - | ||
LIST EMPLIST IN FILE EMPLOYEE INOUT) | LIST EMPLIST IN FILE EMPLOYEE INOUT) | ||
%TEMP IS FIXED DP 2 | %TEMP IS FIXED DP 2 | ||
WAGES1: IN EMPLOYEE FIND ALL RECORDS FOR WHICH | WAGES1: IN EMPLOYEE FIND ALL RECORDS FOR WHICH | ||
NAME = %E.NAME | NAME = %E.NAME | ||
Line 395: | Line 500: | ||
RETURN | RETURN | ||
END SUBROUTINE | END SUBROUTINE | ||
END | END | ||
</p> | </p> | ||
<p>In the following example, an entire array is passed as a single subroutine parameter:</p> | <p> | ||
In the following example, an entire array is passed as a single subroutine parameter:</p> | |||
<p class="code">BEGIN | <p class="code">BEGIN | ||
%NUMBERS IS FLOAT ARRAY(10,10) | %NUMBERS IS FLOAT ARRAY(10,10) | ||
%MAX IS FLOAT | %MAX IS FLOAT | ||
DECLARE SUBROUTINE MAXIMUM(FLOAT ARRAY(*,*), | DECLARE SUBROUTINE MAXIMUM(FLOAT ARRAY(*,*), FLOAT OUTPUT) | ||
. | . | ||
. | . | ||
Line 410: | Line 515: | ||
. | . | ||
. | . | ||
SUBROUTINE MAXIMUM(%ARR IS FLOAT ARRAY(*,*), | SUBROUTINE MAXIMUM(%ARR IS FLOAT ARRAY(*,*), %M IS FLOAT OUTPUT) | ||
%I IS FIXED | %I IS FIXED | ||
%J IS FIXED | %J IS FIXED | ||
%M = 0 | %M = 0 | ||
FOR %I FROM 1 TO $ | FOR %I FROM 1 TO $arrsize('%ARR',1) | ||
FOR %J FROM 1 TO $ | FOR %J FROM 1 TO $arrsize('%ARR',2) | ||
IF %ARR(%I,%J) GT %M THEN | IF %ARR(%I,%J) GT %M THEN | ||
%M = %ARR(%I,%J) | %M = %ARR(%I,%J) | ||
Line 424: | Line 528: | ||
END FOR | END FOR | ||
END SUBROUTINE | END SUBROUTINE | ||
END | END | ||
</p> | </p> | ||
<p>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 | ==Sharing common elements== | ||
====Scope of elements | <p> | ||
<p>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.</p> | 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 <var>Declare</var> statement or by using the <var>Common</var> keyword on a <var><i>%variable</i> Is</var> declaration. For an element to be shared, it must be declared as common in every place (each separate scope) in which it is used. </p> | ||
<p>The elements of the main request (the statements not enclosed by any | |||
<p class="note"><b>Note:</b> In [[Object oriented programming in SOUL|SOUL OOP]], the <var>Common</var> keyword is used for non-class properties and subroutines, which are typically defined for more confined, special-purpose uses (such as migration from SOUL to object-oriented SOUL). See [[Local and Common entities#Common methods and aliases|Common methods and aliases]].</p> | |||
<p>The following elements can be shared if they are declared as common:</p> | |||
===Scope of elements=== | |||
<p>You can share %variables if a | <p> | ||
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.</p> | |||
<p>You can share a list if a | <p> | ||
The elements of the main request (the statements not enclosed by any <var>Subroutine</var>/<var>End Subroutine</var> 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. </p> | |||
<p>You can share found sets if the label of the | |||
===Shareable elements=== | |||
<p> | |||
The following elements can be shared if they are declared as common:</p> | |||
====%variables and %variable arrays==== | |||
<p> | |||
You can share %variables if a <var>Declare <i>%variable</i></var> or a <var><i>%variable</i> Is</var> 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 <var>Field Save</var> option in each declaration. </p> | |||
====Lists of records==== | |||
<p> | |||
You can share a list if a <var>Declare</var> 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 <var class="product">Model 204</var> declares the list implicitly without the common attribute.</p> | |||
====Found sets==== | |||
<p> | |||
You can share found sets if the label of the <var>Find</var> statement is declared as common. To effectively share a found set, follow these steps: </p> | |||
<ol> | <ol> | ||
<li>Add a | <li>Add a <var>Declare</var> statement that declares the statement label as common before the actual label in the portion of the request where the <var>Find</var> statement will be executed. </li> | ||
<li>Add a | |||
<li>Add a <var>Declare</var> 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. </li> | |||
</ol> | </ol> | ||
<p>After execution of the | <p> | ||
After execution of the <var>Find</var> statement, you can access the record set in any portion of the request that contains the proper <var>Declare</var> statement. </p> | |||
<p>You can share a menu or screen as common under the following conditions: </p> | |||
====Menus and screens==== | |||
<p> | |||
You can share a menu or screen as common under the following conditions: </p> | |||
<ul> | <ul> | ||
<li>The first reference to the menu or screen must be the complete definition of the menu or screen, with the | <li>The first reference to the menu or screen must be the complete definition of the menu or screen, with the <var>Common</var> keyword following the <var>Menu</var> or <var>Screen</var> statement. </li> | ||
<li>Other parts of the request can reference the same menu or screen by using an abbreviated declaration of the form: | <li>Other parts of the request can reference the same menu or screen by using an abbreviated declaration of the form: | ||
<p class="code"> | <p class="code">Declare [Menu <i>menuname</i> | Screen <i>screenname</i>] Common </p> | ||
</p> | <p> | ||
<p>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 | 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 <var>Common</var> keyword also result in a compiler error. </p> | ||
<p>If you use a menu or screen %variable (:%variable) within a complex subroutine to refer to a | <p> | ||
If you use a menu or screen %variable (:%variable) within a complex subroutine to refer to a <var>Common</var> screen element, you must include a <var>Common</var> declaration for each possible value of the menu or screen name. </p> | |||
</li> | </li> | ||
</ul> | </ul> | ||
<p>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:</p> | ====Images==== | ||
<p> | |||
The sharing of [[Images|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: </p> | |||
<ul> | <ul> | ||
<li>You can use the | <li>You can use the <var>Common</var> keyword on only the first <var>Image</var> statement of an image block. All other images within the same block are automatically considered as candidates for sharing as common data.</li> | ||
<li>All other parts of the request that access common images must contain the following abbreviated form of the | <li>All other parts of the request that access common images must contain the following abbreviated form of the <var>Declare</var> statement for each image to which access is required: | ||
<p class=" | <p class="syntax">Declare Image <i>imagename</i> Common | ||
</p></li> | </p></li> | ||
</ul> | </ul> | ||
===Declare statement=== | |||
<p> | |||
You can use the <var>Declare</var> statement to perform the following functions: </p> | |||
<ul> | |||
<li>Specify subroutine formal parameter types. </li> | |||
<li>Specify variables, labels, lists, menus, screens, and images as <var>Common</var>. </li> | |||
<p> | </ul> | ||
<p> | |||
Menus and screens are discussed in detail in [[Full-screen feature]]. </p> | |||
<p> | |||
Images are discussed in detail in [[Images]]. </p> | |||
<ul> | <ul> | ||
<li> | <li>To declare lists without having to use an <code>In <i>filename</i> Clear List <i>listname</i></code> clause. See [[Lists#Creating and clearing a list|Creating and clearing a list]]. </li> | ||
< | |||
< | |||
</ | |||
</ | |||
<li>To declare %variables, see [[Using variables and values in computation#Declaring|Declaring %variables and %variable arrays]]. </li> | |||
<li>To declare %variables, see [[Using variables and values in computation]]. | |||
</li> | |||
</ul> | </ul> | ||
<p> | ====Syntax==== | ||
< | <p> | ||
In the context of sharable elements, the format of the <var>Declare</var> statement is shown below. For information about using <var>Declare</var> for global elements, see [[Global features#Global objects|Global objects]]. For the detailed syntax of <var>Declare</var>, see [[Statement syntax#Declare|Statement syntax]]. | |||
</p> | </p> | ||
<p>where declaration is one of the following:</p> | <p class="syntax">Declare <span class="term">declaration</span> | ||
<p class=" | </p> | ||
<p> | |||
where <span class="term">declaration</span> is one of the following:</p> | |||
<p class="syntax">Label <span class="term">label</span> Common | |||
[ | [List] <span class="term">listname</span> [In [File [Perm | Temp] Group] <span class="term">name</span>] [Common] | ||
Image <span class="term">imagename</span> Common | |||
Menu <span class="term">menuname</span> Common | |||
Screen <span class="term">screenname</span> Common | |||
<span class="term">%variable</span> [Is] {Fixed [Dp <span class="term">n</span>] | Float} | |||
[Array(<span class="term">d1</span> [,<span class="term">d2</span> [,<span class="term">d3</span>]])] [Common] | |||
%variable [ | <span class="term">%variable</span> [Is] String [Len <span class="term">n</span>] [Dp {<span class="term">dn1</span> | *}] | ||
[ | [Array(<span class="term">d1</span> [,<span class="term">d2</span> [,<span class="term">d3</span>]])] [No Field Save] [Common] | ||
Subroutine <span class="term">subname</span>[(<span class="term">type</span> [<span class="term">inout-option</span>] [,...]) ] | |||
</p> | |||
====Example==== | |||
<p> | |||
For example, the <var>Declare</var> statement declares the list <code>RECNAMES</code> in the following request: </p> | |||
<p class="code">BEGIN | <p class="code">BEGIN | ||
DECLARE LIST RECNAMES | DECLARE LIST RECNAMES | ||
Line 519: | Line 651: | ||
. | . | ||
. | . | ||
SUBROUTINE REGION(LIST RGNLST OUTPUT, | SUBROUTINE REGION(LIST RGNLST OUTPUT, %REGION IS STRING) | ||
R1: IN EMPLOYEE FIND ALL RECORDS FOR WHICH REGION = %REGION | |||
R1: IN EMPLOYEE FIND ALL RECORDS FOR WHICH | |||
R2: PLACE RECORDS IN R1 ON LIST RGNLIST | R2: PLACE RECORDS IN R1 ON LIST RGNLIST | ||
END SUBROUTINE | END SUBROUTINE | ||
END | END | ||
</p> | </p> | ||
<p>You can define common variables at the beginning of all programs without later redefinitions. The syntax lets you use the % | ===Defining common variables=== | ||
<p>However, if attributes, such as | <p> | ||
<p>For example | You can define common variables at the beginning of all programs without later redefinitions. The syntax lets you use the <var><i>%var</i> Is Common</var> clause without duplicating the previous attributes. </p> | ||
<p> | |||
However, if attributes, such as <var>String</var> or <var>Len</var>, 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. </p> | |||
<p> | |||
For example: </p> | |||
<p class="code">BEGIN | <p class="code">BEGIN | ||
VARIABLES ARE STRING LEN 20 | |||
%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 ?/ | %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 | %Y IS COMMON | ||
END SUBROUTINE | |||
PRINT %X | |||
CALL A | |||
END | |||
</p> | </p> | ||
<p>The following examples illustrate the differences between data that is locally scoped and data that is shared using the | ===Shared common element examples=== | ||
<p> | |||
<p>In this example, the variable %I assumes different values depending upon which part of the request is being executed:</p> | The following examples illustrate the differences between data that is locally scoped and data that is shared using the <var>Common</var> keyword. </p> | ||
====Example 1==== | |||
<p> | |||
In this example, the variable <code>%I</code> assumes different values depending upon which part of the request is being executed: </p> | |||
<p class="code">BEGIN | <p class="code">BEGIN | ||
%I IS FIXED | %I IS FIXED | ||
Line 574: | Line 714: | ||
END FOR | END FOR | ||
END SUBROUTINE | END SUBROUTINE | ||
END | END | ||
</p> | </p> | ||
<p>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:</p> | ====Example 2==== | ||
<p> | |||
In this example, the screen <code>EXAMPLE</code> and the <code>%A</code> and <code>%B</code> variables retain the same values no matter which part of the request is being executed: </p> | |||
<p class="code">BEGIN | <p class="code">BEGIN | ||
SCREEN EXAMDEF COMMON | SCREEN EXAMDEF COMMON | ||
Line 597: | Line 739: | ||
. | . | ||
. | . | ||
END | END | ||
</p> | </p> | ||
<p>In this example, the main request and the subroutine share the common data of an array and a found set:</p> | ====Example 3==== | ||
<p> | |||
In this example, the main request and the subroutine share the common data of an array and a found set:</p> | |||
<p class="code">BEGIN | <p class="code">BEGIN | ||
%COM.ARRAY IS STRING LEN 10 ARRAY(10,10) COMMON | %COM.ARRAY IS STRING LEN 10 ARRAY(10,10) COMMON | ||
Line 619: | Line 763: | ||
CALL EXAMPLE | CALL EXAMPLE | ||
STOP | STOP | ||
SUBROUTINE EXAMPLE | SUBROUTINE EXAMPLE | ||
%COM.ARRAY IS STRING LEN 10 ARRAY(10,10) COMMON | %COM.ARRAY IS STRING LEN 10 ARRAY(10,10) COMMON | ||
DECLARE LABEL ALL.RECS COMMON | DECLARE LABEL ALL.RECS COMMON | ||
%I IS FIXED | %I IS FIXED | ||
%I = 0 | %I = 0 | ||
%J = 2 | %J = 2 | ||
Line 631: | Line 775: | ||
END FOR | END FOR | ||
END SUBROUTINE | END SUBROUTINE | ||
END | END | ||
</p> | </p> | ||
<p>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 | <p> | ||
=== | In the preceding example, the main request updates column 1 of the array <code>%COMM.ARRAY</code> with the contents of <code>FLD</code>. The subroutine, <code>EXAMPLE</code>, updates column 2 of the same array with the contents of <code>FLD2</code>. Both the array <code>%COM.ARRAY</code> and the found set <code>ALL.RECS</code> are shared by using the <var>Common</var> keyword. <code>%I</code> and <code>%J</code> are both local variables. <code>%I</code> is local because it was declared without the <var>Common</var> keyword; <code>%J</code> is local because it was not declared at all. </p> | ||
<p>An | |||
==<b id="On statement"></b>On units== | |||
<p>To define an | <p> | ||
<p class=" | An <var>On</var> unit lets you specify a course of action following a triggering event, such as the terminal operator pressing one of the <var>Attention</var> identifier (AID) keys. The <var>On</var> unit provides an application with a way to override the normal system response. </p> | ||
====Syntax==== | |||
<p> | |||
To define an <var>On</var> unit, use an <var>On</var> block in the following format:</p> | |||
<p class="syntax">[<span class="term">label</span>] On <span class="term">unittype</span> | |||
<span class="term">statement</span> | |||
<span class="literal">...</span> | |||
End On | |||
</p> | </p> | ||
<p>where unittype is one of the following:</p> | <p> | ||
where <var class="term">unittype</var> is one of the following:</p> | |||
<table> | <table> | ||
<tr class="head"> | <tr class="head"> | ||
Line 646: | Line 799: | ||
<th>You can specify the course of action to take if...</th> | <th>You can specify the course of action to take if...</th> | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td>ATTENTION</td> | <td>ATTENTION</td> | ||
<td>The end user invokes the attention feature (for example, presses the <var>BREAK, ATTN,</var> or <var>PA1</var> key, or enters <var>*CANCEL</var>). | <td>The end user invokes the attention feature (for example, presses the <var>BREAK, ATTN,</var> or <var>PA1</var> key, or enters <var>*CANCEL</var>). </td> | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td>ERROR</td> | <td>ERROR</td> | ||
<td><var class="product">Model 204</var> 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]]. | <td><var class="product">Model 204</var> 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]]. </td> | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td>FIELD CONSTRAINT | <td nowrap>FIELD CONSTRAINT | ||
CONFLICT (FCC)</td> | CONFLICT (FCC)</td> | ||
<td>There are field level constraint conflicts. Violating the UNIQUE and AT-MOST-ONE attributes causes field-level conflicts.*</td> | <td>There are field level constraint conflicts. Violating the UNIQUE and AT-MOST-ONE attributes causes field-level conflicts.*</td> | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td>FIND CONFLICT</td> | <td>FIND CONFLICT</td> | ||
<td>A conflict arises evaluating a FIND statement or a FOR EACH RECORD statement used for retrieval.</td> | <td>A conflict arises evaluating a FIND statement or a FOR EACH RECORD statement used for retrieval.</td> | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td>MISSING FILE</td> | <td>MISSING FILE</td> | ||
<td>A remote file is no longer available.</td> | <td>A remote file is no longer available.</td> | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td>MISSING MEMBER</td> | <td>MISSING MEMBER</td> | ||
<td>A remote optional member is no longer available.</td> | <td>A remote optional member is no longer available.</td> | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td>RECORD LOCKING CONFLICT</td> | <td>RECORD LOCKING CONFLICT</td> | ||
Line 679: | Line 836: | ||
</tr> | </tr> | ||
</table> | </table> | ||
<p>*If you have procedures written for <var class="product">Model 204</var> 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. </p> | <p> | ||
<p>Several $functions provide information about conflicts. They are:</p> | *If you have procedures written for <var class="product">Model 204</var> 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. </p> | ||
<p> | |||
Several $functions provide information about conflicts. They are:</p> | |||
<table> | <table> | ||
<tr> | <tr> | ||
<td>[[$ | <td>[[$UpdFile]]</td> | ||
<td>[[$ | <td>[[$UpdFld]]</td> | ||
<td>[[$ | <td>[[$UpdOval]]</td> | ||
<td>[[$ | <td>[[$UpdRec]]</td> | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td>[[$ | <td>[[$UpdStat]]</td> | ||
<td>[[$ | <td>[[$UpdStmt]]</td> | ||
<td>[[$ | <td>[[$UpdVal]]</td> | ||
<td>[[$ | <td>[[$UnqRec]]</td> | ||
</tr> | </tr> | ||
</table> | </table> | ||
<p>The body of the | ===Body of an On unit=== | ||
<p> | |||
<p>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:</p> | The body of the <var>On</var> unit consists of statements immediately following the <var>On</var> statement. Any User Language statement can appear within an <var>On</var> unit except the SUBROUTINE statement.</p> | ||
<p class=" | |||
===Ending an On unit=== | |||
<p> | |||
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:</p> | |||
<p class="syntax">END ON [<span class="term">label</span>] | |||
</p> | </p> | ||
<p>where label is the label of the statement that began the ON statement. | <p> | ||
where <var class="term">label</var> is the label of the statement that began the ON statement. </p> | |||
<p>When an | |||
===Processing an On unit=== | |||
<p>Note the following considerations when using | <p> | ||
When an <var>On</var> statement is evaluated, <var class="product">Model 204</var> remembers the location of the <var>On</var> unit body (the statement after the <var>On</var> statement and within the unit), but does not immediately evaluate the body. Instead, it passes control to the statement immediately following the <var>On</var> unit. The <var>On</var> unit body is evaluated only when the triggering event takes place.</p> | |||
===Usage guidelines for On units=== | |||
<p> | |||
Note the following considerations when using <var>On</var> units:</p> | |||
<ul> | <ul> | ||
<li>You must define the | <li>You must define the <var>On</var> unit before it is invoked (that is, you should typically place the <var>On</var> unit near the beginning of your procedure). </li> | ||
<li>A subsequent definition of an | <li>A subsequent definition of an <var>On</var> 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.</li> | ||
<li>You can jump to destinations within the same | <li>You can jump to destinations within the same <var>On</var> unit. </li> | ||
<li>You can jump out of an | <li>You can jump out of an <var>On</var> unit only to unnested, labeled statements.</li> | ||
<li>You cannot jump into an | <li>You cannot jump into an <var>On</var> unit.</li> | ||
<li>You can jump outside of an ON unit only if the | <li>You can jump outside of an ON unit only if the <var>On</var> unit is part of a main routine or part of a complex subroutine. </li> | ||
<li>You cannot jump out of an | <li>You cannot jump out of an <var>On</var> unit contained in a simple subroutine, regardless of whether the destination is within the subroutine or back in the mainline program, because <var>On</var> units are part of the mainline program; hence, there is no scoping of ON units. This restriction ensures that the <var>On</var> unit is executed and the control returned to the main routine.</li> | ||
<li>An <var>On</var> 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 <var>On</var> units. (See [[Large request considerations#Rules for request continuation|Rules for request continuation]].)</li> | |||
<li>For complex subroutines, an active <var>On</var> unit is temporarily disabled when a subroutine is called that contains an <var>On</var> 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 <var>On</var> unit at the time of the last CALL statement as soon as the RETURN statement is evaluated.</li> | |||
<li><var>On</var> units coded inside complex subroutines can contain JUMP statements that specify a destination outside the <var>On</var> unit. The destination must be to an unnested labeled statement within the complex subroutine. Also, if the <var>On</var> unit is to be jumped out of, the condition that causes the <var>On</var> unit to be invoked must be raised in the subroutine that contains the <var>On</var> unit. | |||
<p> | |||
This prevents a lower level subroutine from returning inadvertently by raising a condition for which there is an <var>On</var> unit coded in a higher level subroutine. If the inadvertent return is attempted, the request is canceled. </p> | |||
</li> | </li> | ||
<li> | <li><var>On</var> 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 <var>On</var> unit that is compiled within a complex subroutine is considered part of only that subroutine. | ||
</ | |||
</li> | </li> | ||
</ul> | </ul> | ||
<p>In the following example, if the user presses the<var> ATTN</var> 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. </p> | ====Example==== | ||
<p> | |||
In the following example, if the user presses the<var> ATTN</var> key instead of entering a response to the prompt in the GET.REC.TYPE statement, the <code>ON ATTENTION</code> 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. </p> | |||
<p class="code">%FLAG=0 | <p class="code">%FLAG=0 | ||
ON ATTENTION | ON ATTENTION | ||
%FLAG=1 | %FLAG=1 | ||
BYPASS | |||
END ON | END ON | ||
GET.REC.TYPE: %TYPE=$READ('ENTER RECORD TYPE') | GET.REC.TYPE: %TYPE=$READ('ENTER RECORD TYPE') | ||
Line 749: | Line 921: | ||
. | . | ||
END.REQUEST: | END.REQUEST: | ||
END | END | ||
</p> | </p> | ||
<p>Only one | ===Only one On unit at a time=== | ||
<p> | |||
<p><var class="product">Model 204</var> passes control to an | Only one <var>On</var> 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 <var>On</var> unit can be redefined within another <var>On</var> unit.</p> | ||
<p>The BYPASS statement handles the various unittypes of an | ===Passing control to and from On units=== | ||
<p> | |||
<var class="product">Model 204</var> passes control to an <var>On</var> 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. </p> | |||
====BYPASS statement==== | |||
<p> | |||
The BYPASS statement handles the various unittypes of an <var>On</var> statement as follows:</p> | |||
<table> | <table> | ||
<tr class="head"> | <tr class="head"> | ||
Line 763: | Line 941: | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td>ON MISSING FILE | <td>ON MISSING FILE<br> | ||
ON MISSING MEMBER</td> | ON MISSING MEMBER</td> | ||
<td>Returns control to the statement immediately after the END FOR statement that closes the current FOR loop. </td> | <td>Returns control to the statement immediately after the END FOR statement that closes the current FOR loop. </td> | ||
Line 772: | Line 950: | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
<td>ON ATTENTION | <td nowrap>ON ATTENTION<br> | ||
ON FIND CONFLICT | ON FIND CONFLICT<br> | ||
ON RECORD LOCKING CONFLICT</td> | ON RECORD LOCKING CONFLICT</td> | ||
<td>Returns control to the statement immediately after the statement that invoked the | <td>Returns control to the statement immediately after the statement that invoked the <var>On</var> unit.</td> | ||
</tr> | </tr> | ||
</table> | </table> | ||
<p>The format of the BYPASS statement is as follows:</p> | <p> | ||
<p class=" | The format of the BYPASS statement is as follows:</p> | ||
<p class="syntax">BYPASS [PENDING STATEMENT] | |||
</p> | </p> | ||
<p>where the PENDING STATEMENT keyword is optional. If the | <p> | ||
where the PENDING STATEMENT keyword is optional. If the <var>On</var> unit is not ended with a BYPASS statement, <var class="product">Model 204</var> automatically generates a STOP statement at the end of the unit. </p> | |||
<p>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.</p> | |||
====CONTINUE statement==== | |||
<p>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 [[Flow of control in User Language#Branching statements|Branching statements]] and [[#Simple subroutines|Simple subroutines]]. </p> | <p> | ||
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.</p> | |||
<p>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.</p> | |||
<p>The format of the RETRY statement is as follows:</p> | ====JUMP TO statement==== | ||
<p class=" | <p> | ||
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 [[Flow of control in User Language#Branching statements|Branching statements]] and [[#Simple subroutines|Simple subroutines]]. </p> | |||
====RETRY statement==== | |||
<p> | |||
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.</p> | |||
<p> | |||
The format of the RETRY statement is as follows:</p> | |||
<p class="syntax">RETRY [PENDING STATEMENT] | |||
</p> | </p> | ||
<p>where the PENDING STATEMENT keyword is optional.</p> | <p> | ||
where the <var>PENDING STATEMENT</var> keyword is optional.</p> | |||
<p>The STOP statement is used to end the request. </p> | |||
====STOP statement==== | |||
<p>You can issue the following statement to clear the definition of an | <p> | ||
<p class=" | The STOP statement is used to end the request. </p> | ||
===Clearing On units=== | |||
<p> | |||
You can issue the following statement to clear the definition of an <var>On</var> unit: </p> | |||
<p class="syntax">CLEAR ON <span class="term">unittype</span> | |||
</p> | </p> | ||
<p>This statement clears any defined | <p> | ||
This statement clears any defined <var>On</var> unit of the type specified. Clearing an ON unit produces the following results:</p> | |||
<ul> | <ul> | ||
<li>After a CLEAR ON ATTENTION, pressing one of the ATTENTION identifier (AID) keys at the terminal does not invoke the ON ATTENTION unit.</li> | <li>After a CLEAR ON ATTENTION, pressing one of the ATTENTION identifier (AID) keys at the terminal does not invoke the ON ATTENTION unit.</li> | ||
<li>After a CLEAR ON ERROR, a request cancellation error does not invoke the ON ERROR unit. </li> | <li>After a CLEAR ON ERROR, a request cancellation error does not invoke the ON ERROR unit. </li> | ||
</ul> | </ul> | ||
<p>An | <p> | ||
An <var>On</var> unit can be defined, cleared, and then redefined. </p> | |||
<p>The | |||
===Pausing during the request=== | |||
<p> | |||
The <var>[[Pause statement|Pause]]</var> statement can be used with <var>On</var> units to cause the request to wait a specified number of seconds and then to retry the statement that caused the evaluation of the <var>On</var> unit. <var>Pause</var> is typically used with the other <var>On</var> unit types (<var>On Record Locking Conflict</var> and <var>On Find Conflict</var>) and is discussed in [[Record level locking and concurrency control]]. </p> | |||
<p>The <var>[[$WakeUp]]</var> function is an alternative approach; it offers millisecond resolution pausing.</p> | |||
</div> <!-- end of toc limit div --> | |||
[[Category:SOUL]] | [[Category:SOUL]] |
Latest revision as of 00:05, 30 May 2018
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
As described later, 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 SOUL 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 SOUL 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:
%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)
- Array %variable formatted as:
%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(*,*))
- A list of records formatted as:
[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)
- Scalar (nonarray) %variable formatted as:
- 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
Expressions can contain field names, screen items, image items, and constants.
- Array %variable
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.
- A list
An 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.
- %variable or expression
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.
Passing arguments to INPUT parameters might involve truncation where the number of decimal places is different, or conversion to 0 for non-numeric strings.
- 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:
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.
Syntax
The complete syntax for the DECLARE SUBROUTINE statement is:
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]
- Scalar %variable of the following format:
- 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.
DECLARE before CALL
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.
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
DECLARE parameters and CALL parameters
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
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.
Note: In SOUL OOP, the Common keyword is used for non-class properties and subroutines, which are typically defined for more confined, special-purpose uses (such as migration from SOUL to object-oriented SOUL). See Common methods and aliases.
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 an image 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 Declaring %variables and %variable arrays.
Syntax
In the context of sharable elements, the format of the Declare statement is shown below. For information about using Declare for global elements, see Global objects. For the detailed syntax of Declare, see Statement syntax.
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 VARIABLES ARE STRING LEN 20 %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 BYPASS 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 number of seconds and then to retry the statement that caused the evaluation of the On unit. Pause is typically used with the other On unit types (On Record Locking Conflict and On Find Conflict) and is discussed in Record level locking and concurrency control.
The $WakeUp function is an alternative approach; it offers millisecond resolution pausing.