Control structures in CTRL

An instruction within a control structure can be made up of one individual command or several commands. In the latter case the commands must be grouped together in curly brackets.

if-else

The if instruction is used to make execution of commands dependent on the value of a test expression.

Syntax

if (<Test expression>)
<instruction1>
else
<instruction2>

Instruction1 is only executed if the test expression is true (returns a value that does not equal zero). Otherwise the instruction is skipped and execution continues with instruction2 in the optional else section. If there is no else section, the command that follows instruction1 is executed if the test expression is false.

Example

main()
{
  int rc;
  float x, y, z;
  dpGet ("Valve17.adjustment:_original.._value", x);
  
  if (x > 0) 
// depends on original value x
  {
    y = 1; 
// y and z are set
    z = 2;
  }
  else if(x < 0)
  {
    y = -1;
    z = 0;
  }
  else
  {
    y = 0;
    z = -1;
  }
  
  DebugN(x, y, z);
}

switch

The switch instruction enables a program to branch according to the value of a test expression.

Syntax

switch(<Test expression>)
{
  case <expression1>: 
<instruction1>
  case <expression2>: 
<instruction2>....]
  [default: Default instruction]
}

The commands that are executed are all those contained in that case section whose relational expression has the same value as the test expression. If none of the relational expressions in the switch construct meet this condition, then only the instruction in the optional "default" section is executed. If there is no default part, execution continues with the instruction following the switch construct. In order to prevent executing all the other instructions in the switch construct after performing an instruction in a case section, a break statement must be used to escape it.

Note that only expressions whose value is known at the parse time (constant expressions) are allowed. Such expressions are:

1. Constants , for example, 4 or "example"

2. Expressions which lead to a constant , for example, 4 * 2 or "example1" + "example2"

3. Constant variables that were defined in the concerned script , for example, const int i =4; or const string f = "example";

NOT ALLOWED are expressions whose value can be determined first at runtime. Such expressions are:

1. Arithmetic operations with two constants, for example:

const string f = "Beispiel"; //constant variable

const string c = "example1"; //constant variable

//Code ......

case f + c :

2. Functions (example())

3. Not constant variables (int i;)

4. If conditions (i > 0 ? 1 : 2);

Example

main()
{
  float y =2;
  string s = "ab";
  const string f = "example1"; 
//constant variable
  const string c = "example2";
  string fo = "example1";
 
  switch(y)
  {
    case 1.0 + 1.0 : 
//expression that leads to a constant
    DebugN("case 1 = 2");
    break;
    
    case 4.2 + 5.3 : 
//expression that leads to a constant
    DebugN("case 2= 9");
    break;
    
    default:
    DebugN("DEFAULT OF FLOAT");
  }
 
  switch(s)
  {
    case "a" + "b" :  
//expression that leads to a constant
    DebugN("a+b of a String");
    break;
     
    case "a":         
//expression that leads to a constant
    DebugN("a of a String"); 
    break; 
    
    default:
    DebugN("DEFAULT OF A STRING");
  }
 
  switch(fo)
  {
    case f :
    DebugN("Constant F"); 
//Constant variable 
    break;
    
    case c :
    DebugN("Constant C"); 
//Constant variable 
    break;
    
    default:
    DebugN("DEFAULT OF THE CONSTANT");
  }
}

while

The while instruction is used for repeated execution of commands according to the value of a test expression.

Syntax

while (<Test expression>)
<instructionW>

InstructionW is only executed if the test expression is true (returns a value that does not equal zero). As soon as the instruction has been executed the test expression is evaluated again. If it is still true, instructionW is executed again. If the test expression is false, instructionW is skipped, and execution continues with the next command.

Example

The following script calculates the factorial of 10 (10! = 1*2*3 ...*10).

main()
{
  int i,n,nfact;
  n = 10;
  nfact = 1;
  i = 1;
  
  while (i<=n) 
// as long as i <= 10
  {
    nfact*=i; 
// multiplied by i
    i++; 
// and then i is incremented by one
  }
  DebugN(nfact); 
// Result 10! = 3628 800
}

for

The for instruction creates program loops that are executed as long as the test expression is true.

Syntax

for ([<expression1>]; [<Test expression>]; [<expression3>])
<instructionF>

A for instruction consists of a condition and an instruction. The condition has to be fulfilled before the instruction is carried out. As a first step the Expression1 from the condition is evaluated. Normally this is the initialization of a counter variable. the second step is the evaluation of the test expression. If this expression is true (returns a value that does not equal zero), InstructionF is executed. As soon as the instruction has been executed expression3 is evaluated. This is usually and incremental increase or decrease of the counter variable. Then the test expression is evaluated again. If it is still true, instructionF is executed again. If the test expression is false, instructionF is skipped, and execution continues with the command following the for instruction. Each of the three expressions in the condition statement is optional. It is possible that the expressions 1 and 3 have already been implemented elsewhere in the code. If there is no test expression then the condition is always considered true. This leads to an endless loop, as the first of the following examples shows:

Example of an endless loop

main()
{
  int i,n,nfact;
  n = 10;
  nfact = 1;
  
  for (i=1;;i++) 
// leads to an endless loop!!
    nfact *= i;
  
  DebugN(nfact); 
// no result is output because of the endless loop
}

Example of an ending loop

main()
{
  int i,n,nfact;
  n = 10;
  nfact = 1;
  
  for(i=1; i<=n; i++)
    nfact *= i;
  
  DebugN(nfact); 
// Result 10! = 3 628 800
}

In the languages C and C++, which CONTROL is based on it is possible to use multiple elements in the expressions within the condition. This behavior is dependent on the use of the comma as an operator. In CONTROL this comma-operator is not implemented. Therefore it is not possible to use more than one element in each of the expressions.

do-while

The do instruction is used for repeated execution of commands according to the truth value of a test expression, although the commands are always executed at least once.

Syntax

do
<instructionD>
while (<Test expression>)

As soon as instructionD has been executed the test expression is evaluated again. If it is true (returns a non-zero value) instructionD is executed again. If the test expression is false, execution continues with the next command.

Example

main()
{
  int i,n,nfact;
  n = 10;
  nfact = 1;
  i = 0;
  
  do
  {
    ++i;
    nfact *= i;
  }
  while(i<=n);
  
  DebugN(nfact);
}

break

The break command is used for exiting switch instructions, and for, while and do loops prematurely.

Syntax

break;

After a break instruction, execution continues with that command that follows the innermost enclosing do, while or for loop, or switch instruction.

continue

The continue command is used for performing the next iteration of for, while and do loops prematurely.

Syntax

continue;

After a continue instruction, execution continues at the end of the innermost enclosing do, while or for loop. At the same time all expressions in the loop condition are evaluated again.

Example

for (i=0; i < len; i++)
{
  if (field[i] == 0)
  continue;
  field[i] = 1 / field[i];
}

try - catch/finally - throw

The try-catch statement provides catching of exceptions, which are either thrown explicitly by throw() or were triggered by script errors (e.g. not declared variable, index out of range, division by zero, etc.).

Syntax

try
{
  statements;
  throw(errClass e)      
// optionally
}
catch 
{
  statements;
}
finally 
{
  statements;
}

The code/statements, which are "tried" and eventually throw an exception, are defined in a try-block. If an exception occurs during the execution of these statements, a catch-block or a finally-block is executed.

After a try-block, a catch-block, a finally-block or both together must be used in the catch-finally order.

The catch-block catches the exception and deletes it. If a finally-block follows the catch-block, this is executed after the catch-block.

An exception can be rethrown in a catch-block by using:

throw(getLastException());

This will preserve the existing stacktrace in the exception.

The finally-block is executed regardless of whether an exception was thrown or a catch-block was specified. The finally-block is useful when, in each case a specific code, must be executed (e.g. variable reset), even if the exception cannot be processed at this point.

The
throw(errClass e)
function throws an exception ('e'). An exception is always of the type errClass.

Exception in the finally-/catch-block

If an exception occurs in the finally-block, the treatment is canceled and the block continues the "roll up" till the next try-catch/-finally block is found. An exception is always replaced by the newest/current exception.

If an exception occurs in the catch-block, the treatment is canceled, but an eventually existing finally-block is executed before "rolling up" the block in order to find a new try-catch/-finally block. An exception is always replaced by the newest/actual exception.

If no other try-catch/-finally block is found during the "roll up", the thread is exited and an error message is returned ("uncaught exception ...").

Example

main()
{
  try
  {
    DebugN("main: try");
    
//throw(makeError("", PRIO_SEVERE, ERR_PARAM, 54, "In main:try"));
    foo();
    DebugN("main:try end");
  }
  
  catch
  {
    DebugN("main: catch");
    DebugN(getLastException())
    
//throw(makeError("", PRIO_SEVERE, ERR_PARAM, 54, "In main: catch"));
    DebugN("main: catch end");
  }
  
  finally
  {
    DebugN("main: finally");
    
//throw(makeError("", PRIO_SEVERE, ERR_PARAM, 54, "In main: finally"));
    DebugN("main: finally end");
  }
  DebugN("main: end");
}
  
foo()
{
  try
  {
    DebugN("foo: try");
    
//throw(makeError("", PRIO_SEVERE, ERR_PARAM, 54, "In foo:try"));
    DebugN("foo: try end");
  }
  finally
  {
    DebugN("foo: finally");
    
//throw(makeError("", PRIO_SEVERE, ERR_PARAM, 54, "In foo: finally"));
    DebugN("foo: finally end");
  }
  
  DebugN("foo: end");
}