    [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.