3.3. Control Statements in Nickle

3.3.1. Simple statements

expr;
; /* null statement */
{ statement ... }

The simplest statement is merely an expression terminated by a semicolon; the expression is evaluated. A semicolon by itself is allowed but does nothing. One or more statements may be grouped inside curly braces to form one compound statement; each is executed in order. Any statements may compose the statement list, including control statements and other curly-bracketed lists.

3.3.2. Conditionals

if ( expr ) statement
else statement

if is used to execute a section of code only under some condition: If expr is true, statement is executed; otherwise control skips over it. For example:

if ( x == 0 )
	printf ( "x is zero.\n" );

In this case, the message will be printed only if x is zero.

else allows for a choice if the condition fails. It executes its statement if the most recent if or twixt (see below) did not. For example,

if ( x == 0 )
	printf ( "x is zero.\n" );
else
	printf ( "x is not zero.\n" );

More than one option may be presented by nesting further 'if's in 'else' statements like this:

if ( x == 0 )
	printf ( "x is zero.\n" );
else if ( x < 0 )
	printf ( "x is negative.\n" );
else
	printf ( "x is positive.\n" );

3.3.3. Twixt

twixt ( expr; expr ) statement

twixt, like if, executes statement only if the first expr is true. If statement is executed, the second expr is evaluated after it. That order is gauranteed - even if statement throws an exception or long_jmps out of the twixt, the second expr will be evaluated. Thus, twixt is useful in locked operations where the statement should only be executed if a lock was successfully acquired and the lock must be released afterward:

twixt ( get_lock ( ); release_lock ( ) )
	locked_operation ( );

'Else' statements may bind to 'twixt's as well as 'if's, providing choices such as:

twixt ( get_lock ( ); release_lock ( ) )
	locked_operation ( );
else
	printf ( "failed to acquire lock.\n" );

3.3.4. Switch

switch ( expr ) { case expr: statement-list ... default: statement-list }

Control jumps to the first case whose expr evaluates to the same value as the expr at the top. Unlike in C, these values do not have to be integers, or even constant. The optional case default matches any value. If nothing is matched and there is no default, control skips the switch entirely. This example prints out a number to the screen, replacing it by a letter as though it were a poker card:

switch ( x ) {
	case 1:
		printf ( "A\n" );	/* ace */
		break;
	case 11:
		printf ( "J\n" );	/* jack */
		break;
	case 12:
		printf ( "Q\n" );	/* queen */
		break;
	case 13:
		printf ( "K\n" );	/* king */
		break;
	default:
		printf ( "%d\n", x );	/* numeric */
		break;
}

Notice the breaks in the example. Once control jumps to the matching case, it continues normally: Upon exhausting that statement-list, it does not jump out of the switch; it continues through the subsequent statement lists. Here is an example of this 'falling through':

int x = 3;

switch ( sign ( x ) ) {
	case -1:
		printf ( "x is negative.\n" );
	case 1:
		printf ( "x is positive.\n" );
	default:
		printf ( "x is zero.\n" );
}

This prints:

x is positive.
x is zero.

Falling through may be desirable if several cases are treated similarly; however, it should be used sparingly and probably commented so it is clear you are doing it on purpose. This is a difficult error to catch.

3.3.5. Union switch

union switch ( union ) { case name: statement-list ... default: statement-list }

union switch is similar to switch. It matches its cases based on what name currently applies to the union's value. As always, default matches everything. The following example chooses the best way to print the union:

union {
	int a;
	string b;
} u;

u.b = "hello";

union switch ( u ) {
	case a:
		printf ( "%d\n", u.a );
		break;
	case b:
		printf ( "%s\n", u.b );
		break;
}

In this case, it prints 'hello'.

An additional name may follow that of a case; the union's value will be available inside the case by that name. The switch above could have been written:

union switch ( u ) {
	case a num:
		printf ( "%d\n", num );
		break;
	case b str:
		printf ( "%s\n", str );
		break;
}

3.3.6. Loops

while ( expr ) statement
do statement while ( expr )
for ( expr; expr; expr ) statement

while executes statement repeatedly as long as expr is true. Control continues outside the loop when expression becomes false. For example:

int x = 0;
while ( x < 10 ) {
	printf ( "%d\n", x );
	++x;
}

This prints the numbers from zero to nine.

do-while is like while, but tests the condition after each iteration rather than before. Thus, it is garaunteed to execute at least once. It is often used in input while testing for end-of-file:

file f = File::open ( "test", "r" );

do {
	printf ( "%s\n", File::fgets ( f ) );
} while ( ! end ( f ) );

close ( f );

for begins by evaluating the first expr, which often initializes a counter variable; since declarations are expressions in Nickle, they may be used here and the counter will be local to the loop. Then it executes statement as long as the second expr is true, like while. After each iteration, the third expr is evaluated, which usually increments or decrements the counter variable. The while example above can also be written as the following for loop:

for ( int x = 0; x < 10; ++x )
	printf ( "%d\n", x );

3.3.7. Flow control

continue
break
return expr

continue restarts the nearest surrounding do-while, while, or for loop by jumping directly to the conditional test. The iterative statement of a for loop will be evaluated first.

break leaves the nearest surrounding do-while, while, for, or switch statement by jumping to its end. The iterative statement of a for loop will not be evaluated.

return returns from the nearest surrounding function with value expr.