9.7 Repetitive Compilation (Compile-Time Loops)

HLA's #while..#endwhile and #for..#endfor statements provide compile-time loop constructs. The #while statement tells HLA to process the same sequence of statements repetitively during compilation. This is very handy for constructing data tables as well as providing a traditional looping structure for compile-time programs. Although you will not employ the #while statement anywhere near as often as the #if statement, this compile-time control structure is very important when you write advanced HLA programs.

The #while statement uses the following syntax:

#while( constant_boolean_expression )
     << text >>
#endwhile

When HLA encounters the #while statement during compilation, it will evaluate the constant boolean expression. If the expression evaluates false, HLA will skip over the text between the #while and the #endwhile clauses (the behavior is similar to the #if statement if the expression evaluates false). If the expression evaluates true, then HLA will process the statements between the #while and #endwhile clauses and then "jump back" to the start of the #while statement in the source file and repeat this process, as shown in Figure 9-3.

HLA compile-time #while statement operation

Figure 9-3. HLA compile-time #while statement operation

To understand how this process works, consider the program in Example 9-2.

Example 9-2. #while..#endwhile demonstration

program ctWhile;
#include( "stdlib.hhf" )

static
ary: uns32[5] := [ 2, 3, 5, 8, 13 ];

begin ctWhile;

     ?i := 0;
     #while( i < 5 )

          stdout.put( "array[ ", i, " ] = ", ary[i*4], nl );
          ?i := i + 1;

     #endwhile

end ctWhile;

As you can probably surmise, the output from this program is the following:

array[ 0 ] = 2
array[ 1 ] = 3
array[ 2 ] = 4
array[ 3 ] = 5
array[ 4 ] = 13

What is not quite obvious is how this program generates this output. Remember, the #while..#endwhile construct is a compile-time language feature, not a runtime control construct. Therefore, the previous #while loop repeats five times during compilation. On each repetition of the loop, the HLA compiler processes the statements between the #while and #endwhile clauses. Therefore, the preceding program is really equivalent to the code that is shown in Example 9-3.

Example 9-3. Program equivalent to the code in Example 9-2

program ctWhile;
#include( "stdlib.hhf" )

static
     ary: uns32[5] := [ 2, 3, 5, 8, 13 ];

begin ctWhile;

     stdout.put( "array[ ", 0, " ] = ", ary[0*4], nl );
     stdout.put( "array[ ", 1, " ] = ", ary[1*4], nl );
     stdout.put( "array[ ", 2, " ] = ", ary[2*4], nl );
     stdout.put( "array[ ", 3, " ] = ", ary[3*4], nl );
     stdout.put( "array[ ", 4, " ] = ", ary[4*4], nl );

end ctWhile;

As you can see in this example, the #while statement is very convenient for constructing repetitive-code sequences. This is especially invaluable for unrolling loops.

HLA provides three forms of the #for..#endfor loop. These three loops take the following general form:

Example 9-4. HLA #for loops

#for( valObject := startExpr to endExpr )

          .
          .
     #endfor

     #for( valObject := startExpr downto endExpr )
          .
          .
          .
     #endfor

     #for( valObject in composite_expr )
          .
          .
          .
     #endfor

As its name suggests, valObject must be an object you've defined in a val declaration.

For the first two forms of the #for loop above, the startExpr and endExpr components can be any HLA constant expression that yields an integer value. The first of these #for loops is semantically equivalent to the following #while code:

?valObject := startExpr;
     #while( valObject <= endExpr )
               .
               .
               .
          ?valObject := valObject + 1;
  #endwhile

The second of these #for loops is semantically equivalent to the #while loop:

?valObject := startExpr;
     #while( valObject >= endExpr )
               .
               .
               .
     ?valObject := valObject - 1;
  #endwhile

The third of these #for loops (the one using the in keyword) is especially useful for processing individual items from some composite data type. This loop repeats once for each element, field, character, and so on of the composite value you specify for composite_expr. This can be an array, string, record, or character set expression. For arrays, this #for loop repeats once for each element of the array and on each iteration of the loop; the loop control variable contains the current element's value. For example, the following compile-time loop displays the values 1, 10, 100, and 1,000:

#for( i in [1, 10, 100, 1000])
          #print( i )
 #endfor

If the composite_expr constant is a string constant, the #for loop repeats once for each character in the string and sets the value of the loop control variable to the current character. If the composite_expr constant expression is a record constant, then the loop will repeat once for each field of the record, and for each iteration the loop control variable will take on the type and value of the current field. If the composite_expr expression is a character set, the loop will repeat once for each character in the set, and the loop control variable will be assigned that character.

The #for loop actually turns out to be more useful than the #while loop because the larger number of compile-time loops you encounter repeat a fixed number of times (for example, processing a fixed number of array elements, macro parameters, and so on).