Summary:
See also: Exceptions, Programs, Functions, Reports, Expressions
The CALL instruction invokes a specified function or method.
CALL [ prefix. ] function ( [ parameter [,...]
] ) [
RETURNING variable [,...] ]
If the IMPORT [FGL] instruction was used to import a module, function can be prefixed with the name of the module followed by a dot (i.e. module.function). The module prefix is required to fully-qualify the function in case of conflicts (i.e. when functions with the same name are defined in several modules).
The RETURNING clause assigns values returned by the function to variables in the calling routine. The RETURNING clause is only needed when the function returns parameters. If the function returns a unique parameter, the CALL func(...) RETURNING var instruction can be replaced by a LET var = function(...) statement. A function returning a single parameter can also be used in expressions.You can use a double-pipe operator ' || ' to pass the concatenation of character expressions as a parameter.
Note that the value of a receiving variable may be different from the value returned by the function, following the data conversion rules.01
MAIN02
DEFINE var1 CHAR(10)03
DEFINE var2 CHAR(2)04
LET var1 = foo()05
DISPLAY "var1 = " || var106
CALL foo() RETURNING var207
DISPLAY "var2 = " || var208
END MAIN09
10
FUNCTION foo()11
RETURN "Hello"12
END FUNCTION
01
MAIN02
DEFINE var1 CHAR(15)03
DEFINE var2 CHAR(15)04
CALL foo() RETURNING var1, var205
DISPLAY var1, var206
END MAIN07
08
FUNCTION foo()09
DEFINE r1 CHAR(15)10
DEFINE r2 CHAR(15)11
LET r1 = "return value 1"12
LET r2 = "return value 2"13
RETURN r1, r214
END FUNCTION
01
MAIN02
DEFINE r1 RECORD03
id1 INTEGER,04
id2 INTEGER,05
name CHAR(30)06
END RECORD07
CALL get_name( NULL, NULL, NULL ) RETURNING r1.*08
CALL get_name( NULL, r1.id2, r1.name ) RETURNING r1.*09
CALL get_name( r1.* ) RETURNING r1.*10
DISPLAY r1.name11
CALL get_name( 1, 2, "John" ) RETURNING r1.id2, r1.id1, r1.name12
DISPLAY r1.name13
END MAIN14
15
FUNCTION get_name( code1, code2, name )16
DEFINE code1 INTEGER17
DEFINE code2 INTEGER18
DEFINE name CHAR(30)19
IF code1 IS NULL THEN20
LET name = "ERROR:code1 is NULL"21
LET code2 = NULL22
ELSE23
IF code2 IS NULL THEN24
LET name = "ERROR:code2 is NULL"25
LET code1 = NULL26
ELSE27
IF name IS NULL THEN28
LET name = "SMITH"29
END IF30
END IF31
END IF32
RETURN code1, code2, name33
END FUNCTION
The RETURN instruction transfers the control back from a function with optional return values.
RETURN [ value [,...] ]
value can be a variable, a literal, a constant or any valid expression.
Record members can be returned with the .* or THRU notation. Each member is returned as an independent variable.
A function may have several RETURN points (not recommended in structured programming) but they must all return the same number of values.
The number of returned values must correspond to the number of variables listed in the RETURNING clause of the CALL statement invoking this function.
A function cannot return an array.
01
MAIN02
DEFINE forname, surname CHAR(10)03
CALL foo(NULL) RETURNING forname, surname04
DISPLAY forname CLIPPED, " ", upshift(surname) CLIPPED05
CALL foo(1) RETURNING forname, surname06
DISPLAY forname CLIPPED, " ", upshift(surname) CLIPPED07
END MAIN08
09
FUNCTION foo(code)10
DEFINE code INTEGER11
DEFINE person RECORD12
name1 CHAR(10),13
name2 CHAR(20)14
END RECORD15
IF code IS NULL THEN16
RETURN NULL, NULL17
ELSE18
LET person.name1 = "John"19
LET person.name2 = "Smith"20
RETURN person.*21
END IF22
END FUNCTION
The CASE instruction specifies statement blocks that must be executed conditionally.
CASE expression-1
WHEN expression-2
{ statement | EXIT CASE }
[...]
[ OTHERWISE
{ statement | EXIT CASE }
[...]
]
END CASE
CASE
WHEN boolean-expression
{ statement | EXIT CASE }
[...]
[ OTHERWISE
{ statement | EXIT CASE }
[...]
]
END CASE
A NULL expression is considered as FALSE: When doing a CASE expr ... WHEN [NOT] NULL using the syntax 1, it always evaluates to FALSE. Use syntax 2 as CASE ... WHEN expr IS NULL to test if an expression is null.
Make sure that expression-2 is not a boolean expression when using the first syntax. The compiler will not raise an error in this case, but you might get unexpected results at runtime.
If there is more than one expression-2 matching expression-1 (syntax 1), or if two boolean expressions (syntax 2) are true, only the first matching WHEN block will be executed.01
MAIN02
DEFINE v CHAR(10)03
LET v = "C1"04
CASE v05
WHEN "C1"06
DISPLAY "Value is C1"07
WHEN "C2"08
DISPLAY "Value is C2"09
WHEN "C3"10
DISPLAY "Value is C3"11
OTHERWISE12
DISPLAY "Unexpected value"13
END CASE14
END MAIN
01
MAIN02
DEFINE v CHAR(10)03
LET v = "C1"04
CASE05
WHEN ( v="C1" OR v="C2" )06
DISPLAY "Value is either C1 or C2"06
WHEN ( v="C3" OR v="C4" )07
DISPLAY "Value is either C3 or C4"08
OTHERWISE09
DISPLAY "Unexpected value"10
END CASE11
END MAIN
The CONTINUE instruction transfers the program execution from a statement block to another location in the compound statement that is currently being executed.
CONTINUE { FOR | FOREACH | MENU |
CONSTRUCT | INPUT | WHILE }
CONTINUE instruction can only be used within the statement block specified by instruction. For example, CONTINUE FOR can only be used within a FOR ... END FOR statement block.
The CONTINUE FOR, CONTINUE FOREACH, or CONTINUE WHILE keywords cause the current FOR, FOREACH, or WHILE loop (respectively) to begin a new cycle immediately. If conditions do not permit a new cycle, however, the looping statement terminates.
The CONTINUE CONSTRUCT and CONTINUE INPUT statements cause the program to skip all subsequent statements in the current control block. The screen cursor returns to the most recently occupied field in the current form, giving the user another chance to enter data in that field.
The CONTINUE MENU statement causes the program to ignore the remaining statements in the current MENU control block and redisplay the menu. The user can then choose another menu option.
Note that CONTINUE INPUT is valid in INPUT and INPUT ARRAY statements.
01
MAIN02
DEFINE i INTEGER03
LET i = 004
WHILE i < 505
LET i = i + 106
DISPLAY "i=" || i07
CONTINUE WHILE08
DISPLAY "This will never be displayed !"09
END WHILE10
END MAIN
The FOR instruction executes a statement block a specified number of times.
FOR counter = start TO finish [
STEP value ]
{ statement | EXIT FOR | CONTINUE
FOR }
[...]
END FOR
counter is a variable of type INTEGER or SMALLINT that serves as an index for the FOR statement block.
start is an integer expression used to set an initial counter value.
finish is any valid integer expression used to specify an upper limit for counter.
value is any valid integer expression whose value is added to counter after each iteration of the statement block.
When the STEP keyword is not given, counter is incremented by 1.
The FOR instruction block executes the statements up to the END FOR keyword a specified number of times, or until EXIT FOR terminates the FOR statement. You can use the CONTINUE FOR instruction to skip the following statements and continue with the loop.
The runtime system maintains the counter, whose value changes on each pass through the statement block. On the first iteration through the loop, this counter is set to the initial expression at the left of the TO keyword. For all further iterations, the value of the increment expression in the STEP clause specification (1 by default) is added to the counter in each pass through the block of statements. When the sign of the difference between the values of counter and the finish expression at the right of the TO keyword changes, the runtime system exits from the FOR loop.
The FOR loop terminates after the iteration for which the left- and right-hand expressions are equal. Execution resumes at the statement following the END FOR keywords. If either expression returns NULL, the loop cannot terminate, because the Boolean expression "left = right" cannot become TRUE.
If the FOR loop includes one or more SQL statements that modify the database, then it is advisable that the entire FOR loop be within a transaction. You may also PREPARE the SQL statements before the loop to increase performance.value = 0 causes an unending loop unless there is an adequate EXIT FOR statement.
NULL for start, finish or value is treated as 0. There is no way to catch this as an error.
If statement modifies the value of counter, you might get unexpected results at runtime. In this case, it is recommended that you use a WHILE loop instead.
It is highly recommended that you ensure that statement does not modify the values of start, finish and/or value.01
MAIN02
DEFINE i, i_min, i_max INTEGER03
LET i_min = 104
LET i_max = 1005
DISPLAY "Look how well I can count from " || i_min || " to " || i_max06
DISPLAY "I can count forwards..."07
FOR i = i_min TO i_max08
DISPLAY i09
END FOR10
DISPLAY "... and backwards!"11
FOR i = i_max TO i_min STEP -112
DISPLAY i13
END FOR14
END MAIN
The GOTO instruction transfers program control to a labeled line within the same program block.
GOTO [ : ] label-id
The GOTO statement can be used in a WHENEVER statement to handle exceptions.
The LABEL jump point can be defined before or after the GOTO statement.
Note that the LABEL and GOTO statements must use the label-id within a single MAIN, FUNCTION, or REPORT program block.
01
MAIN02
DEFINE exit_code INTEGER03
DEFINE l_status INTEGER04
WHENEVER ANY ERROR GOTO _error05
DISPLAY 1/006
GOTO _noerror07
LABEL _error:08
LET l_status = STATUS09
DISPLAY "The error number ", l_status, " has occurred."10
DISPLAY "Description : ", err_get(l_status)11
LET exit_code = -112
GOTO :_exit13
LABEL _noerror:14
LET exit_code = 015
GOTO _exit16
LABEL _exit:17
EXIT PROGRAM (exit_code)18
END MAIN
The EXIT instruction transfers control out of a control structure (a block, a loop, a CASE statement, or an interface instruction).
EXIT { CASE | FOR | MENU | CONSTRUCT
| FOREACH | REPORT | DISPLAY | INPUT | WHILE
}
The EXIT instruction instruction must be used inside the control structure specified by instruction. For example, EXIT FOR can only appear inside a FOR ... END FOR program structure.
EXIT DISPLAY exits the DISPLAY ARRAY instruction and EXIT INPUT exits both INPUT and INPUT ARRAY blocks.
To exit a function, use the RETURN instruction. To exit a program, use the EXIT PROGRAM instruction.
01
MAIN02
DEFINE i INTEGER03
LET i = 004
WHILE TRUE05
DISPLAY "This is an infinite loop. How would you get out of here ?"06
LET i = i + 107
IF i = 100 THEN08
EXIT WHILE09
END IF10
END WHILE11
DISPLAY "Well done."12
END MAIN
The IF instruction executes a group of statements conditionally.
IF condition THEN
statement
[...]
[ ELSE
statement
[...]
]
END IF
condition is any boolean expression supported by the language.
If condition is TRUE, the runtime system executes the block of statements following the THEN keyword, until it reaches either the ELSE keyword or the END IF keywords and resumes execution after the END IF keywords.
If condition is FALSE, the runtime system executes the block of statements between the ELSE keyword and the END IF keywords. If ELSE is absent, it resumes execution after the END IF keywords.
To test the equality of integer expressions, both " = " and " == " operators may be used. IF 5 = 5 THEN ... can be written IF 5 == 5 THEN ...
Note that a NULL expression is considered as FALSE. Use the IS NULL keyword to test if an expression is null.
01
MAIN02
DEFINE name CHAR(20)03
LET name = "John Smith"04
IF name MATCHES "John*" THEN05
DISPLAY "The first name is too common to be displayed."06
IF name MATCHES "*Smith" THEN07
DISPLAY "Even the last name is too common to be displayed."08
END IF09
ELSE10
DISPLAY "The name is " || name || "."11
END IF12
END MAIN
The LABEL instruction declares a jump point in the program that can be reached by a GOTO.
LABEL label-id:
label-id is a unique identifier in a MAIN, REPORT, or FUNCTION program block.
The label-id must be followed by a colon (:).
The LABEL instruction declares a statement label, making the next statement one to which a GOTO statement can transfer program control.
01
MAIN02
DISPLAY "Line 2"03
GOTO line504
DISPLAY "Line 4"05
LABEL line5:06
DISPLAY "Line 6"07
END MAIN
The SLEEP instruction causes the program to pause for the specified number of seconds.
SLEEP seconds
seconds is a valid integer expression.
If seconds < 0 or second IS NULL, the program does not stop.
01
MAIN02
DISPLAY "Please wait 5 seconds..."03
SLEEP 504
DISPLAY "Thank you."05
END MAIN
The WHILE statement executes a block of statements while a condition that you specify in a boolean expression is true.
WHILE b-expression
{ statement | EXIT WHILE | CONTINUE
WHILE }
[...]
END WHILE
b-expression is any valid boolean expression.
If b-expression is TRUE, the runtime system executes the statements that follow it, down to the END WHILE keyword. The runtime system again evaluates the b-expression, and if it is still TRUE, the runtime system executes the same statement block. The runtime system usually stops when b-expression becomes FALSE or statement is EXIT WHILE. You can use the CONTINUE WHILE instruction to skip the following statements and continue with the loop.
If b-expression is FALSE, the runtime system passes control to the statement that follows END WHILE.
If b-expression is complex, it is much better to define a boolean [ INTEGER or CHAR(1) ] variable that takes the result of b-expression and use this variable for b-expression.
A WHILE loop can replace a FOR loop : FOR i = 1 TO 5 ; ... ; END FOR is equivalent to LET i = 1 ; WHILE i <= 5 ; ... ; LET i = i + 1 ; END WHILE
In order to avoid unending loops, make sure that either statement will cause b-expression to be FALSE, or that the EXIT WHILE statement will be executed.
01
MAIN02
DEFINE lval INTEGER03
DEFINE lmin INTEGER04
DEFINE lmax INTEGER05
DEFINE lnb INTEGER06
DEFINE lcnt INTEGER07
DEFINE lguess INTEGER08
DISPLAY "NumberGuess program"09
LET lnb = 2010
LET lmin = 011
LET lmax = 100012
LET lval = 753 --random value between lmin and lmax13
DISPLAY "Guess a number between " || lmin || " and " || lmax14
LET lguess = (lmax - lmin) / 215
LET lcnt = 116
WHILE lguess <> lval AND lcnt < lnb17
DISPLAY "\n Attempt number " || lcnt18
DISPLAY " Your guess is " || lguess || ", hopefully between " || lmin || " and " || lmax19
IF lval > lguess THEN20
DISPLAY " Try higher."21
LET lmin = lguess22
ELSE23
DISPLAY " Try lower."24
LET lmax = lguess25
END IF26
LET lguess = lmin + (lmax - lmin) / 227
LET lcnt = lcnt + 128
END WHILE29
IF lcnt >= lnb THEN30
DISPLAY "Sorry, the maximum number of attempts has been reached. The number was " || lval31
ELSE32
DISPLAY "Well done. You have found the number " || lval || " in " || lcnt || " attempts."33
END IF34
END MAIN