Chapter 8. Control Structures

Table of Contents

1. Conditional Execution: The If Instruction
2. The Switch Instruction
3. The For Loop
4. The While Loop
5. The Do-While Loop
6. The Break, Stop and Exit Instructions
7. The Continue Instruction
8. The Include Instruction
9. The Goto Instruction
10. The Lock and Unlock Instructions

Control structures in a programming language allow to execute blocks of code depending on certain conditions, typically expressed as the Boolean result of an expression. Control structures also allow repeated execution of blocks of code without the need for repeating the actual code. This chapter describes the implementation and use of such structures in Biferno.

1. Conditional Execution: The If Instruction

The if instruction has the following syntax:

if (expression)
	{
		execute block 1
	}
else
	{
		execute block 2
	}
    

The if instruction executes the code block between curly brackets if the expression between parentheses returns true. If the expression returns false, code execution will resume from the instruction after the closing curly bracket, or from the next code line if the block consists of one line (brackets around a one-line code block are optional).

The code block after the if instruction can be optionally followed by the else keyword, followed in turn by a code block. The second code block will be executed if the conditional expression returns false. The if instruction corresponds to the natural language conditional sentence: "If this is true, do this, else do that".

The else keyword can also be followed by another if instruction. In this case the code that follows will be executed only if the conditional expression in the second if statement returns true.

The following example clarifies the concept.

<?
	if (a == 0)
		{
			print("Warning! Null data.")
		}
	else if (a < 1 || a > 100)
		{
			print("Error! Values must be between 1 and 100.")
		}
	else
		{
			// Process value of variable a
		}
?>
    

Notice how the condition expressed by the first if is more restrictive than the one of the second. Also, the order of the two statements can not be inverted, otherwise the code block for the case a == 0 will never be executed.

Using a curly bracket to delimit a code block consisting of a single line is optional. Biferno also features the ? ternary operator, also known from the C language. This operator provides a concise syntax to specify simple if-else instructions:

(expression 1) ? (expression 2) : (expression 3)
    

An example is:

x = (a > b) ? a : b
    

This is equivalent to:

if (a > b)
	x = a
else
	x = b
    

The code assigns to the x variable the largest of the values of the a and b variables.

2. The Switch Instruction

The if instruction allows to concatenate a sequence of logical tests by using the if- else if-else construct. This technique can produce code that is difficult to read and error-prone when it is necessary to execute a different block of code for each of the many values that a variable can assume. In this case is often preferable to use the switch instruction with the following syntax:

switch (main expression)
{
	case expression_1:
		execute block 1
		break
	case expression_2:
		execute block 2
		break
	...
	case expression_n:
		execute block n
		break
	default:
		execute default block
}

The main expression is calculated first and its value is compared with the values of the expressions following the case instruction, in the order in which such expressions are written. If the value of the main expression is equal to the value of one of these expressions, the code block between the semicolon on the expression line and the end of the entire switch instruction (delimited by the closing curly bracket) is executed. All following case instructions are ignored. Limiting the execution to the single code block corresponding to the comparison expression is possible by ending the block with the break instruction, which causes execution to resume with the first instruction following the switch statement.

If the comparison between the result of the main expression and the results of the case expressions yields no match, no code block is executed, unless the switch statement has a default clause (usually the last) followed by a code block. This code block will be executed if the main expression does not match any case expression. This mechanism is similar to the last else instruction of the if-else if-else construct: if none of the logical tests is true, then execute a default code block.

The following sample script translates and prints the names of the days of the week from Italian into English. The script assumes that the day variable of the string class has been initialized, e.g. by user input (passing parameters to a Biferno script is discussed in Chapter 18, Passing Parameters between Pages).

<?
	switch (day)
	{
		case "Lunedì":
			print("Monday")
			break
		case "Martedì":
			print("Tuesday")
			break
		case "Mercoledì":
			print("Wednesday")
			break
		case "Giovedì":
			print("Thursday")
			break
		case "Venerdì":
			print("Friday")
			break
		case "Sabato":
			print("Saturday")
			break
		case "Domenica":
			print("Sunday")
			break
		default:
			print("Error!	The string does not \
contain the name of a day of the week")
	}
?>
    

Expressions following the case instructions must be static constants properties of a class, or literal expressions. They can never be object (of type var or const).

3. The For Loop

Loops allow to repeat the execution of a code block depending on a logical condition. All iterative control loop instructions present in other languages are implemented in Biferno: for, while and do-while. The fundamental difference between a for and a while loop is in the way the number of iterations is defined and controlled. Usually, for loops are used when the number of iterations to be executed can be determined a priori. while loops are used when the number of iterations to be executed can not be determined in advance, but depends on the value of one or more logical conditions.

The for instruction has the following base syntax:

for (initial value; control condition; increment)
	{
		code block to be repeated
	}
    

The instructions between round brackets usually consist in the initialization of an integer index, followed by an expression that must be verified (i.e. assume the true value) to allow continuation of the loop execution, followed by an expression that increments (or decrements) the index value.

The following example shows a simple for loop that calculates the sum of the integers between 1 and 10.

<?
	sum = 0
	for (i = 1; i <= 10; i++)
		{
			sum += i
		}
?>
    

Outside of the loop the integer variable sum is initialized to 0. The loop index i is initialized to 1. The control condition verifies if i is less or equal to 10 to continue the loop. With every iteration of the loop, the current value of the i index is added to the sum variable. At the end of the loop execution the value of the sum variable will be 55, i.e. we have executed the following expression:

1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 = 55.
    

Notice that after the last iteration of the loop the value of the i index will be 11, because this is the first value that causes the control condition i <= 10 to assume the value false, which stops the loop.

More than one element can be specified in a single for instruction for the initial value and the increment. We can write:

<?
	for (i = 1, j = 10; j > 0; i += j, j--)
		{
			print(i + ":" + j)
			if (j > 1)
				print (", ")
		}
?>
    

This script produces the following result:

1:10, 11:9, 20:8, 28:7, 35:6, 41:5, 46:4, 50:3, 53:2, 55:1
    

4. The While Loop

The while instruction has the following syntax:

while (control condition)
	{
		code block to be repeated
	}
    

The control condition between round brackets must be verified (i.e. assume the true value) to allow execution of the code block between curly brackets.

The value of the expression implementing the control condition is computed before each repetition of the loop. This implies that the loop code block is never executed if the expression is initially false.

For the same reason the loop code block must contain instructions that modify the value of the expression used as the control condition (or the break instruction, as we will see), otherwise the exit condition might never become true, producing an infinite loop (in Biferno the execution of a script is subject to a time limitation, after which it is forcibly stopped).

A simple example of a while loop printing the integer numbers from 1 to 10 is:

<?
	i = 1
	while (i <= 10)
		{
			print(i + "<br>\n")
			i++
		}
?>
    

It is easy to see that, unlikely the for loop, the i variable is initialized outside of the loop and incremented inside the loop block code. In this way, when the i variable assumes the value 11, the condition i <= 10 is false and the loop is interrupted.

5. The Do-While Loop

The do-while instruction has the following syntax:

do
	{
		code block to be repeated
	}
while(control condition)
    

The fundamental difference between the do-while and the while instructions is that in the do-while the control of the condition is performed at the end of each repetition. This implies that the code block to be repeated is always executed at least once.

6. The Break, Stop and Exit Instructions

The use of the break instruction to interrupt the execution of a code block has been seen with respect to the switch instruction. In general, the break instruction can be used to forcibly exit a loop and continue the execution of the code starting from the first instruction after the loop block.

Sometimes it can be necessary to interrupt the execution of a script before all its instructions have been executed. In Biferno this is realized via the stop and exit instructions. The two instructions are different. The exit instruction interrupts the execution of the entire script. The stop instruction interrupts the execution of the ".bfr" file which is being processed by the server when the stop instruction is executed. If the current file has been included in another Biferno file using the include instruction (see the Section 8, “The Include Instruction”), processing will resume with the instruction following the include directive in the including file.

7. The Continue Instruction

The continue instruction can be used to interrupt the execution of the current iteration of a loop. This instruction immediately interrupts execution of the current iteration and restarts execution from the first instruction of the loop. If the current iteration is the last one, execution resumes from the instruction following the loop code block that has called the continue instruction. Execution of the continue instruction outside of a loop generates the Err_IllegalUseOfKeyword error.

8. The Include Instruction

This instruction is actually a predefined Biferno function that takes a file path as a parameter. When this instruction is executed in a script, execution resumes with the first instruction of the included file. If the included file is a Biferno script, its instructions are executed as if they would have been contained in the including file. When execution of the included file terminates, processing continues with the first instruction following the include instruction in the including script. For this reason, if a file to be included contains Biferno code, the code must always be delimited by the <? and ?> tags as if the code would be in a stand alone script.

To better understand the include function we show an example of its use within a for loop. Assume we have written three files to be included as follows. The "include1.bfr" file contains the following code:

<?
	s = "include1" // initialize s variable of class string
?>
    

The "include2.bfr" file contains the following code:

<?
	s += " include2" // concatenate a string with variable s
?>
    

The "include3.bfr" file contains the following code:

<?
	s += " include3" // concatenate another string with variable s
?>
    

Our main script contains a loop that includes all three files in sequence. The name of the file to be included at each iteration is constructed using the loop index.

<?
	for (i = 1; i <= 3; i++)
		{
			include("include" + i + ".bfr")
		}
?>
    

When execution of this script terminates, the s variable has the value: "include1 include2 include3".

This example shows how the include function implements branching in the execution of a script. The use of this function avoids the repetition of code blocks that have to be executed many times in different scripts.

9. The Goto Instruction

This instruction allows to stop execution of a script and to resume it in a different point within the same script or code block within a single file. The goto instruction can not be used to jump to a given position in a file included via the include instruction. The goto instruction has the following syntax:

goto label
    

where “label” identifies the code line where processing must resume. To label a code line we write the label name followed by the ":" character (semicolon). A simple example is the following script that prints the integer numbers from 1 to 10 separated by a space:

<?
i = 1
start:
	if (i > 10)
		goto end
	print(i++ + " ")
goto start
end:
?>
    

When the goto start instruction is executed, the execution stops and is resumed with the instruction following the start label, i.e. the if instruction. Similarly, the goto end instruction causes a jump to the line following the end label, i.e. the end of the script. It should be apparent that this script can be written in a much more elegant and natural way using the for loop.

It has been proved that every program containing goto instructions can be rewritten using only the fundamental control structures. Moreover, the use of the goto instruction makes it difficult to understand program structure. We advise to avoid using this instruction, which has been implemented in Biferno only for reasons of completeness.

10. The Lock and Unlock Instructions

Usually Biferno scripts are executed concurrently, i.e. it is possible that two or more users request the same script to the server and that the scripts is executed in parallel by different Biferno threads. Biferno is a multi-threaded application, i.e. capable to start multiple execution instances within a single process.

The default behavior can be avoided if necessary by isolating code segments that should never be executed concurrently by multiple users.

This is accomplished in Biferno by using the lock and unlock instructions. The script code enclosed by the lock and unlock is executed by Biferno for a single user at a time. All other execution requests relative to that code segment are queued and will be executed one by one in sequence.

The lock instruction is incremental in that it can be used multiple times within the same script, as long as there is a matching unlock instruction for every lock instruction. In any case, to avoid server blocking, Biferno calls automatically the unlock instruction for every lock that is still unbalanced when the script execution terminates. The lock and unlock instructions are often used to preserve data integrity when accessing files or database in read/write mode.