Back to Contents


Flow Control

Summary:

See also: Exceptions, Programs, Functions, Reports, Expressions


CALL

Purpose:

The CALL instruction invokes a specified function or method.

Syntax:

CALL [ prefix. ] function ( [ parameter [,...] ] ) [ RETURNING variable [,...] ]

Notes:

  1. prefix can be an imported BDL module, an imported C Extension module, a built-in class, a variable referencing an object of a built-in class, a Java class, a variable referencing a Java object.
  2. function can a function defined in one of the modules of the program, a C function defined in a C-extension module, a built-in function of the language, a built-in class or object method of the language or a Java class or object method of an imported Java class.
  3. parameter can be a variable, a literal, a constant or any valid expression, including object references of built-in or Java classes.
  4. parameters are separated by a comma ' , '.
  5. variable is a variable receiving a value returned by the function.

Usage:

The CALL instruction invokes the function or class/object method specified and passes the program flow control to that function/method. After the called function was executed, the flow control goes back to the caller, the runtime system executing the next statement that appears after the CALL instruction.

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.

Example 1: Function returning a single value

01 MAIN
02   DEFINE var1 CHAR(10)
03   DEFINE var2 CHAR(2)
04   LET var1 = foo()
05   DISPLAY "var1 = " || var1
06   CALL foo() RETURNING var2
07   DISPLAY "var2 = " || var2
08 END MAIN
09 
10 FUNCTION foo() 
11   RETURN "Hello"
12 END FUNCTION

Example 2: Function returning several values

01 MAIN
02   DEFINE var1 CHAR(15)
03   DEFINE var2 CHAR(15)
04   CALL foo() RETURNING var1, var2
05   DISPLAY var1, var2
06 END MAIN
07 
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, r2
14 END FUNCTION

Example 3: Function and records

01 MAIN
02   DEFINE r1 RECORD
03              id1     INTEGER,
04              id2     INTEGER,
05              name    CHAR(30)
06            END RECORD
07   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.name
11   CALL get_name( 1, 2, "John" ) RETURNING r1.id2, r1.id1, r1.name
12   DISPLAY r1.name
13 END MAIN
14
15 FUNCTION get_name( code1, code2, name )
16   DEFINE code1   INTEGER
17   DEFINE code2   INTEGER
18   DEFINE name    CHAR(30)
19   IF code1 IS NULL THEN
20      LET name = "ERROR:code1 is NULL"
21      LET code2 = NULL 
22   ELSE
23      IF code2 IS NULL THEN
24        LET name = "ERROR:code2 is NULL"
25        LET code1 = NULL 
26      ELSE
27         IF name IS NULL THEN
28            LET name = "SMITH"
29         END IF
30      END IF
31   END IF
32   RETURN code1, code2, name
33 END FUNCTION

RETURN

Purpose:

The RETURN instruction transfers the control back from a function with optional return values.

Syntax:

RETURN [ value [,...] ]

Notes:

  1. value can be a variable, a literal, a constant or any valid expression.

Usage:

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.

Example:

01 MAIN
02   DEFINE forname, surname CHAR(10)
03   CALL foo(NULL) RETURNING forname, surname
04   DISPLAY forname CLIPPED, " ", upshift(surname) CLIPPED
05   CALL foo(1) RETURNING forname, surname
06   DISPLAY forname CLIPPED, " ", upshift(surname) CLIPPED
07 END MAIN
08
09 FUNCTION foo(code)
10   DEFINE code   INTEGER
11   DEFINE person RECORD
12                  name1   CHAR(10),
13                  name2   CHAR(20)
14                END RECORD
15  IF code IS NULL THEN
16     RETURN NULL, NULL
17  ELSE
18     LET person.name1 = "John"
19     LET person.name2 = "Smith"
20     RETURN person.*
21  END IF
22 END FUNCTION

CASE

Purpose:

The CASE instruction specifies statement blocks that must be executed conditionally.

Syntax 1:

CASE expression-1
  WHEN expression-2
   
{ statement | EXIT CASE }
    [...]
  [ OTHERWISE 
    { statement | EXIT CASE }
    [...]
  ]
END CASE

Syntax 2:

CASE
  WHEN boolean-expression
   
{ statement | EXIT CASE }
    [...]
  [ OTHERWISE 
    { statement | EXIT CASE }
    [...]
  ]
END CASE

Notes:

  1. expression-1 is any expression supported by the language.
  2. expression-2 is an expression that is tested against expression-1. expression-1 and expression-2 should have the same data type.
  3. boolean-expression is any boolean expression supported by the language.
  4. statement is any instruction supported by the language.

Usage:

In a CASE flow control block, the first matching WHEN block is executed. If there is no matching WHEN block, then the OTHERWISE block is executed. If there is no matching WHEN block and no OTHERWISE block, then the program control jumps to the statement following the END CASE keyword. The EXIT CASE statement transfers the program control to the statement following the END CASE keyword. There is an implicit EXIT CASE statement at the end of each WHEN block and at the end of the OTHERWISE block. The OTHERWISE block must be the last block of the CASE instruction.

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.

Example 1: First syntax

01 MAIN
02    DEFINE v CHAR(10)
03    LET v = "C1"
04    CASE v
05      WHEN "C1"
06        DISPLAY "Value is C1"
07      WHEN "C2"
08        DISPLAY "Value is C2"
09      WHEN "C3"
10        DISPLAY "Value is C3"
11      OTHERWISE
12        DISPLAY "Unexpected value"
13    END CASE
14 END MAIN

Example 2: Second syntax

01 MAIN
02    DEFINE v CHAR(10)
03    LET v = "C1"
04    CASE
05      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      OTHERWISE
09        DISPLAY "Unexpected value"
10    END CASE
11 END MAIN

CONTINUE

Purpose:

The CONTINUE instruction transfers the program execution from a statement block to another location in the compound statement that is currently being executed.

Syntax:

CONTINUE { FOR | FOREACH | MENU | CONSTRUCT | INPUT | WHILE }

Usage:

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.

Example:

01 MAIN
02   DEFINE i INTEGER
03   LET i = 0
04   WHILE i < 5
05     LET i = i + 1
06     DISPLAY "i=" || i
07     CONTINUE WHILE
08     DISPLAY "This will never be displayed !"
09   END WHILE
10 END MAIN

FOR

Purpose:

The FOR instruction executes a statement block a specified number of times.

Syntax:

FOR counter = start TO finish [ STEP value ]
   { statement | EXIT FOR | CONTINUE FOR }
   [...]
END FOR

Notes:

  1. counter is a variable of type INTEGER or SMALLINT that serves as an index for the FOR statement block.

  2. start is an integer expression used to set an initial counter value.

  3. finish is any valid integer expression used to specify an upper limit for counter.

  4. value is any valid integer expression whose value is added to counter after each iteration of the statement block.

  5. When the STEP keyword is not given, counter is incremented by 1.

  6. statement is any instruction supported by the language.
  7. If value is less than 0, counter is decreased. In this case, start should be higher than finish.

Usage:

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.

Example:

01 MAIN
02   DEFINE i, i_min, i_max INTEGER
03   LET i_min = 1
04   LET i_max = 10
05   DISPLAY "Look how well I can count from " || i_min || " to " || i_max
06   DISPLAY "I can count forwards..."
07   FOR i = i_min TO i_max
08       DISPLAY i
09   END FOR
10   DISPLAY "... and backwards!"
11   FOR i = i_max TO i_min STEP -1
12       DISPLAY i
13   END FOR
14 END MAIN

GOTO

Purpose:

The GOTO instruction transfers program control to a labeled line within the same program block.

Syntax:

GOTO [ : ] label-id

Notes:

  1. label-id is the name of the LABEL statement to jump to.

Usage:

GOTO statements can reduce the readability of your program source and result in infinite loops. It is recommended that you use FOR, WHILE, IF, CASE, CALL statements instead.

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.

Example:

01 MAIN
02   DEFINE exit_code INTEGER
03   DEFINE l_status  INTEGER
04   WHENEVER ANY ERROR GOTO _error
05   DISPLAY 1/0
06   GOTO _noerror
07   LABEL _error:
08     LET l_status = STATUS
09     DISPLAY "The error number ", l_status, " has occurred."
10     DISPLAY "Description : ", err_get(l_status)
11     LET exit_code = -1
12   GOTO :_exit
13   LABEL _noerror:
14     LET exit_code = 0
15   GOTO _exit
16   LABEL _exit:
17   EXIT PROGRAM (exit_code)
18 END MAIN

EXIT

Purpose:

The EXIT instruction transfers control out of a control structure (a block, a loop, a CASE statement, or an interface instruction).

Syntax:

EXIT { CASE | FOR | MENU | CONSTRUCT | FOREACH | REPORT | DISPLAY | INPUT | WHILE }

Usage:

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.

Example:

01 MAIN
02   DEFINE i INTEGER
03   LET i = 0
04   WHILE TRUE
05     DISPLAY "This is an infinite loop. How would you get out of here ?"
06     LET i = i + 1
07     IF i = 100 THEN
08       EXIT WHILE
09     END IF
10   END WHILE
11   DISPLAY "Well done."
12 END MAIN

IF

Purpose:

The IF instruction executes a group of statements conditionally.

Syntax:

IF condition THEN
   statement
   [...]
[ ELSE
   statement
   [...]
]
END IF

Notes:

  1. condition is any boolean expression supported by the language.

  2. statement is any instruction supported by the language.

Usage:

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.

Example:

01 MAIN
02   DEFINE name CHAR(20)
03   LET name = "John Smith"
04   IF name MATCHES "John*" THEN 
05      DISPLAY "The first name is too common to be displayed."
06      IF name MATCHES "*Smith" THEN
07         DISPLAY "Even the last name is too common to be displayed."
08      END IF
09   ELSE
10      DISPLAY "The name is " || name || "."
11   END IF
12 END MAIN

LABEL

Purpose:

The LABEL instruction declares a jump point in the program that can be reached by a GOTO.

Syntax:

LABEL label-id:

Notes:

  1. label-id is a unique identifier in a MAIN, REPORT, or FUNCTION program block.

  2. The label-id must be followed by a colon (:).

Usage:

The LABEL instruction declares a statement label, making the next statement one to which a GOTO statement can transfer program control.

Example:

01 MAIN
02   DISPLAY "Line 2"
03   GOTO line5
04   DISPLAY "Line 4"
05   LABEL line5:
06   DISPLAY "Line 6"
07 END MAIN

SLEEP

Purpose:

The SLEEP instruction causes the program to pause for the specified number of seconds.

Syntax:

SLEEP seconds

Notes:

  1. seconds is a valid integer expression.

Usage:

The SLEEP instruction is typically invoked to let the end user read a message displayed on a character terminal. With graphical applications, the SLEEP command is seldom used.

If seconds < 0 or second IS NULL, the program does not stop.

Example:

01 MAIN
02   DISPLAY "Please wait 5 seconds..."
03   SLEEP 5
04   DISPLAY "Thank you."
05 END MAIN

WHILE

Purpose:

The WHILE statement executes a block of statements while a condition that you specify in a boolean expression is true.

Syntax:

WHILE b-expression
   { statement | EXIT WHILE | CONTINUE WHILE }
   [...]
END WHILE

Notes:

  1. b-expression is any valid boolean expression.

  2. statement is any instruction supported by the language.

Usage:

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.

Example:

01 MAIN
02   DEFINE lval INTEGER
03   DEFINE lmin INTEGER
04   DEFINE lmax INTEGER
05   DEFINE lnb  INTEGER
06   DEFINE lcnt INTEGER
07   DEFINE lguess INTEGER
08   DISPLAY "NumberGuess program"
09   LET lnb  = 20
10   LET lmin = 0
11   LET lmax = 1000
12   LET lval = 753 --random value between lmin and lmax
13   DISPLAY "Guess a number between " || lmin || " and " || lmax
14   LET lguess = (lmax - lmin) / 2
15   LET lcnt = 1
16   WHILE lguess <> lval AND lcnt < lnb
17     DISPLAY "\n  Attempt number " || lcnt
18     DISPLAY "  Your guess is " || lguess || ", hopefully between " || lmin || " and " || lmax
19     IF lval > lguess THEN
20        DISPLAY "  Try higher."
21        LET lmin = lguess
22     ELSE
23        DISPLAY "  Try lower."
24        LET lmax = lguess
25     END IF
26     LET lguess = lmin + (lmax - lmin) / 2
27     LET lcnt = lcnt + 1
28   END WHILE
29   IF lcnt >= lnb THEN
30      DISPLAY "Sorry, the maximum number of attempts has been reached. The number was " || lval
31   ELSE
32      DISPLAY "Well done. You have found the number " || lval || " in " || lcnt || " attempts."
33   END IF
34 END MAIN