[NAME]
ALL.dao.tutorial.controls

[TITLE]
Constrol Structures

[DESCRIPTION]

Control structures are essential for a program to do complex work. Dao supports the 
common controls such as: if-else, for, while, do-while, switch-case, break and skip 
(continue) etc.

 0.1   If-Else  
The if-else control allows the program to branch and execute different blocks of codes, 
based on the results of the condition expressions.

If a condition is true, the code block that is nested under the if or else if statement 
will be executed:
     
   1  if( expr1 ){
   2     block1;
   3  }else if( expr2 ){
   4     block2;
   5  }else{
   6     block3;
   7  }
     

If expr1 is true, block1 is executed; otherwise, if expr2 is true, block2 is executed; 
otherwise, block3 is executed; zero or more else if and zero or one else statement can 
be used.
     
   1  if( 2 > 1 ) io.writeln("2 is larger than 1.");
     

Before each condition expression, there can be an optional expression or variable
declaration, for example,
     
   1  if( rnd = rand(100); rnd > 50 ) io.writeln( "random number is >50" );
     


 0.2   For  

Dao supports different styles of for-looping, the simplest one is probably the following,
     
   1  for( variable = init_value : step_value : max_value ){
   2     block;
   3  }
     
For this loop, the init_value will be first assigned to variable, and then compared to 
the max_value to test if it is smaller than max_value, if yes, the execution enters the 
loop. After each cycle of the loop, the step_value (or one if step_value is omitted) is 
added to variable, then the comparison and testing is repeated to determine whether to 
enter the loop or exit the loop.

C/C++ style for looping is supported by Dao: 
     
   1  for( init; condition; step ){
   2     block;
   3  }
     
The execution sequence of for statement is the following: 
  1. execute initial expression init, and goto 3;
  2. execute step;
  3. evaluate the condition expression condition;
  4. check the value of condition: if true, goto 5; otherwise, goto 6;
  5. execute block, and goto 2;
  6. stop looping; and start to execute the statements after the loop body.


Dao also supports for-in loop, 
     
   1  for( item in list ){
   2     block;
   3  }
     
Multiple in can appear in one loop, and the items of the same indices from multiple 
lists are taken in each cycle. The loop is terminated after all the items in the shortest
list have been iterated.
     
   1  for( item1 in list1; item2 in list2; ... ){
   2     block;
   3  }
     


for-in can also be used for maps, 
     
   1  for( item in a_map ){
   2     block;
   3  }
     


Examples,
     
   1  for( i=0; i<3; ++i ){
   2     io.writeln( i );
   3  }
   4  
   5  hash = { "b" => 11, "a" => 22, "e" => 33, "c" => 44 };
   6  for( a in hash.keys(); b in hash.values(); c in {1 : 1 : hash.size()-1 } ){
   7     #if a == "a" break
   8     io.writeln( a, b, c );
   9  }
     


Note: if a single string is used in the condition expression in if,while,for statements,
it returns true, if the string has length larger than zero, otherwise, returns false.

 0.3   While  

When a condition is true, repeatedly execute a block of codes: 
     
   1  while( expr ){
   2     block;
   3  }
     

If expr is true, block is executed and repeated until expr becomes false, namely, while 
expr is true, execute block.
     
   1  i = 0;
   2  while( i < 5 ){
   3     io.writeln( i );
   4     i += 1;
   5  }
     

Before the condition expression, there can be an optional expression or variable
declaration, for example,
     
   1  while( rnd = rand(100); rnd > 50 ) io.writeln( "random number is >50" );
     


 0.4   Do-While  

     
   1  do{
   2      block;
   3  } while ( condition )
     
Execute block, and then repeat executing it when the condition is true.

 0.5   Switch-Case  

Switch-case control provides a convenient way to branch the code and choose a block of
code to execute based on the value of a object.
     
   1  switch( value ){
   2     case C_1 : block_1
   3     case C_2 : block_2
   4     case C_3 : block_3
   5     ...
   6     default: block0
   7  }
     
If the value equals to C_i, block_i will be executed. Here C_i must be a constant, but 
they can be of different types, that means, you can mix numbers and strings as case
values. Unlike in C/C++, no break statement is required to get out of the switch.

If you want to execute the same block of codes for different case values, you just need
to organize them together in the following way:
     
   1  switch( value ){
   2     case C1, C2, C3 :
   3        block3
   4     ...
   5     default: block0
   6  }
     
Namely, Dao allows one case entry to have multiple values. In this way, block3 will be 
executed for case values C1,C2 and C3. As a simple example, 
     
   1  a = "a";
   2  switch( a ){
   3    case 1, "a" : io.write("case 1 or a");
   4    default :  io.write("case default");
   5  }
     


Dao also allows the use of a value range represented as start ... end as case entry, so 
that the corresponding code block is executed if the value in switch falls inside the
range. The ranges must not be overlapping.
     
   1  switch( 5 ){
   2  case 1 ... 4 : io.writeln( 'case 1-4' );
   3  case 5 ... 9 : io.writeln( 'case 5-9' );
   4  case 10 ... 11 : a = 1;
   5  }
     


 0.6   Break and Skip  

break can be used to exit a loop, and skip can be used to skip the rest part of script 
and start the next cycle of a loop. skip is equivalent to continue in C/C++.
     
   1  for( i=0; i<5; ++i ){
   2      io.writeln( i ); 
   3      if( i == 3 ) break;
   4  }
     


 0.7   Deferred Block  

Deferred block is a block that will not be executed immediately when the normal execution
reaches it, instead, its execution will be deferred until the exit of the function after 
the function is completed or has encounted an exception. Such blocks are marked with the
defer keyword, and placed inside of a pair of curly brackets: 
     
   1  defer { block }
     

This is very useful for freeing resources allocated in functions that have multiple
exits. For example, one can defer a block to close a file handle immediately after the
file handle is opened:
     
   1  routine Test( id )
   2  {
   3      fout = io.writeln( 'output.txt', 'w+' )
   4      defer { fout.close() }
   5  
   6      fout.writeln( 'abc' )
   7      if( id == 0 ){
   8          fout.writeln( '123' )
   9          return
  10      }
  11      io.writeln( 'def' )
  12  }
     


As shown in the above example, a deferred block can access outer scope constants and
varaibles in the same way as closures. These outer scope variables are captured at the
time the deferred block is reached in the normal execution.
     
   1  routine Test()
   2  {
   3      for( i = 1 : 3 ) defer { io.writeln( 'deferred', i ) }
   4  }
   5  Test()
     
This will print:
     
   1  deferred 3
   2  deferred 2
   3  deferred 1
     


When a function exits, all the deferred blocks that have been reached in the normal
execution will be executed in the reverse order of being reached.

Any deferred block can modify the value returned by the function. In order to do this,
one must designate a variable name for the returned value, by placing the name inside a
pair brackets after the defer keyword: 
     
   1  defer (ret) { ret += 1 }
     
For example,
     
   1  routine Test()
   2  {
   3      defer ( result ) {
   4          result *= 2  # double the returning value
   5      }
   6      return 1000;
   7  }
   8  io.writeln( Test() )
     
This will print out 2000.