Next Previous Contents

## 8.Statements

Loosely speaking, a statement is composed of expressions that are grouped according to the syntax or grammar of the language to express a complete computation. A semicolon is used to denote the end of a statement.

A statement that occurs within a function is executed only during execution of the function. However, statements that occur outside the context of a function are evaluated immediately.

The language supports several different types of statements such as assignment statements, conditional statements, and so forth. These are described in detail in the following sections.

## 8.1Variable Declaration Statements

Variable declarations were already discussed in the chapter on Variables. For the sake of completeness, a variable declaration is a statement of the form

``` variable variable-declaration-list ; ```
where the variable-declaration-list is a comma separated list of one or more variable names with optional initializations, e.g.,
``` variable x, y = 2, z; ```

## 8.2Assignment Statements

Perhaps the most well known form of statement is the assignment statement. Statements of this type consist of a left-hand side, an assignment operator, and a right-hand side. The left-hand side must be something to which an assignment can be performed. Such an object is called an lvalue.

The most common assignment operator is the simple assignment operator `=`. Examples of its use include

``` x = 3; x = some_function (10); x = 34 + 27/y + some_function (z); x = x + 3; ```
In addition to the simple assignment operator, S-Lang also supports the binary assignment operators:
``` += -= *= /= &= |= ```
Internally, S-Lang transforms
``` a += b; ```
to
``` a = a + b; ```
Likewise `a-=b` is transformed to `a=a-b`, `a*=b` is transformed to `a=a*b`, and so on.

It is extremely important to realize that, in general, `a+b` is not equal to `b+a`. For example if `a` and `b` are strings, then `a+b` will be the string resulting from the concatenation of `a` and `b`, which generally is not he same as the concatenation of `b` with `a`. This means that `a+=b` may not be the same as `a=b+a`, as the following example illustrates:

``` a = "hello"; b = "world"; a += b; % a will become "helloworld" c = b + a; % c will become "worldhelloworld" ```

Since adding or subtracting 1 from a variable is quite common, S-Lang also supports the unary increment and decrement operators `++`, and `--`, respectively. That is, for numeric data types,

``` x = x + 1; x += 1; x++; ```
are all equivalent. Similarly,
``` x = x - 1; x -= 1; x--; ```
are also equivalent.

Strictly speaking, `++` and `--` are unary operators. When used as `x++`, the `++` operator is said to be a postfix-unary operator. However, when used as `++x` it is said to be a prefix-unary operator. The current implementation does not distinguish between the two forms, thus `x++` and `++x` are equivalent. The reason for this equivalence is that assignment expressions do not return a value in the S-Lang language as they do in C. Thus one should exercise care and not try to write C-like code such as

``` x = 10; while (--x) do_something (x); % Ok in C, but not in S-Lang ```
The closest valid S-Lang form involves a comma-expression:
``` x = 10; while (x--, x) do_something (x); % Ok in S-Lang and in C ```

S-Lang also supports a multiple-assignment statement. It is discussed in detail in the section on Multiple Assignment Statement.

## 8.3Conditional and Looping Statements

S-Lang supports a wide variety of conditional and looping statements. These constructs operate on statements grouped together in blocks. A block is a sequence of S-Lang statements enclosed in braces and may contain other blocks. However, a block cannot include function declarations. In the following, statement-or-block refers to either a single S-Lang statement or to a block of statements, and integer-expression is an integer-valued or boolean expression. next-statement represents the statement following the form under discussion.

### if

The simplest condition statement is the `if` statement. It follows the syntax

``` if (integer-expression) statement-or-block next-statement ```
If integer-expression evaluates to a non-zero (boolean TRUE) result, then the statement or group of statements implied statement-or-block will get executed. Otherwise, control will proceed to next-statement.

An example of the use of this type of conditional statement is

``` if (x != 0) { y = 1.0 / x; if (x > 0) z = log (x); } ```
This example illustrates two `if` statements where the second `if` statement is part of the block of statements that belong to the first.

### if-else

Another form of `if` statement is the if-else statement. It follows the syntax:

``` if (integer-expression) statement-or-block-1 else statement-or-block-2 next-statement ```
Here, if expression evaluates to a non-zero integer, statement-or-block-1 will get executed and control will pass on to next-statement. However, if expression evaluates to zero, statement-or-block-2 will get executed before continuing on to next-statement. A simple example of this form is
``` if (x > 0) z = log (x); else throw DomainError, "x must be positive"; ```
Consider the more complex example:
``` if (city == "Boston") if (street == "Beacon") found = 1; else if (city == "Madrid") if (street == "Calle Mayor") found = 1; else found = 0; ```
This example illustrates a problem that beginners have with if-else statements. Syntactically, this example is equivalent to
``` if (city == "Boston") { if (street == "Beacon") found = 1; else if (city == "Madrid") { if (street == "Calle Mayor") found = 1; else found = 0; } } ```
although the indentation indicates otherwise. It is important to understand the grammar and not be seduced by the indentation!

### ifnot

One often encounters `if` statements similar to

``` if (integer-expression == 0) statement-or-block ```
or equivalently,
``` if (not(integer-expression)) statement-or-block ```
The `ifnot` statement was added to the language to simplify the handling of such statements. It obeys the syntax
``` ifnot (integer-expression) statement-or-block ```
and is functionally equivalent to
``` if (not (expression)) statement-or-block ```

Note: The `ifnot` keyword was added in version 2.1 and is not supported by earlier versions. For compatibility with older code, the `!if` keyword can be used, although its use is deprecated in favor of `ifnot`.

### orelse, andelse

As of S-Lang version 2.1, use of the `andelse` and `orelse` have been deprecated in favor of the `&&` and `||` short-circuiting operators.

The syntax for the `orelse` statement is:

``` orelse {integer-expression-1} ... {integer-expression-n} ```
This causes each of the blocks to be executed in turn until one of them returns a non-zero integer value. The result of this statement is the integer value returned by the last block executed. For example,
``` orelse { 0 } { 6 } { 2 } { 3 } ```
returns 6 since the second block is the first to return a non-zero result. The last two block will not get executed.

The syntax for the `andelse` statement is:

``` andelse {integer-expression-1} ... {integer-expression-n} ```
Each of the blocks will be executed in turn until one of them returns a zero value. The result of this statement is the integer value returned by the last block executed. For example,
``` andelse { 6 } { 2 } { 0 } { 4 } ```
evaluates to 0 since the third block will be the last to execute.

### switch

The switch statement deviates from its C counterpart. The syntax is:

``` switch (x) { ... : ...} . . { ... : ...} ```
The ``:`' operator is a special symbol that in the context of the switch statement, causes the top item on the stack to be tested, and if it is non-zero, the rest of the block will get executed and control will pass out of the switch statement. Otherwise, the execution of the block will be terminated and the process will be repeated for the next block. If a block contains no `:` operator, the entire block is executed and control will pass onto the next statement following the `switch` statement. Such a block is known as the default case.

As a simple example, consider the following:

``` switch (x) { x == 1 : message("Number is one.");} { x == 2 : message("Number is two.");} { x == 3 : message("Number is three.");} { x == 4 : message("Number is four.");} { x == 5 : message("Number is five.");} { message ("Number is greater than five.");} ```
Suppose `x` has an integer value of 3. The first two blocks will terminate at the ``:`' character because each of the comparisons with `x` will produce zero. However, the third block will execute to completion. Similarly, if `x` is 7, only the last block will execute in full.

A more familiar way to write the previous example is to make use of the `case` keyword:

``` switch (x) { case 1 : message("Number is one.");} { case 2 : message("Number is two.");} { case 3 : message("Number is three.");} { case 4 : message("Number is four.");} { case 5 : message("Number is five.");} { message ("Number is greater than five.");} ```
The `case` keyword is a more useful comparison operator because it can perform a comparison between different data types while using `==` may result in a type-mismatch error. For example,
``` switch (x) { (x == 1) or (x == "one") : message("Number is one.");} { (x == 2) or (x == "two") : message("Number is two.");} { (x == 3) or (x == "three") : message("Number is three.");} { (x == 4) or (x == "four") : message("Number is four.");} { (x == 5) or (x == "five") : message("Number is five.");} { message ("Number is greater than five.");} ```
will fail because the `==` operation is not defined between strings and integers. The correct way to write this is to use the `case` keyword:
``` switch (x) { case 1 or case "one" : message("Number is one.");} { case 2 or case "two" : message("Number is two.");} { case 3 or case "three" : message("Number is three.");} { case 4 or case "four" : message("Number is four.");} { case 5 or case "five" : message("Number is five.");} { message ("Number is greater than five.");} ```

### Looping Forms

In this section, the various looping statements are discussed. Each of these statements support an optional `then` clause, which is discussed in a separate section below.

### while

The `while` statement follows the syntax

``` while (integer-expression) statement-or-block [ then statement-or-block ] next-statement ```
It simply causes statement-or-block to get executed as long as integer-expression evaluates to a non-zero result. For example,
``` i = 10; while (i) { i--; newline (); } ```
will cause the `newline` function to get called 10 times. However,
``` i = -10; while (i) { i--; newline (); } ```
would loop forever (or until `i` wraps from the most negative integer value to the most positive and then decrements to zero).

If you are a C programmer, do not let the syntax of the language seduce you into writing this example as you would in C:

``` i = 10; while (i--) newline (); ```
Keep in mind that expressions such as `i--` do not return a value in S-Lang as they do in C. The same effect can be achieved to use a comma to separate the expressions as in
``` i = 10; while (i, i--) newline (); ```

### do...while

The `do...while` statement follows the syntax

``` do statement-or-block while (integer-expression); [ then statement-or-block ] ```
The main difference between this statement and the `while` statement is that the `do...while` form performs the test involving integer-expression after each execution of statement-or-block rather than before. This guarantees that statement-or-block will get executed at least once.

A simple example from the jed editor follows:

``` bob (); % Move to beginning of buffer do { indent_line (); } while (down (1)); ```
This will cause all lines in the buffer to get indented via the jed intrinsic function `indent_line`.

### for

Perhaps the most complex looping statement is the `for` statement; nevertheless, it is a favorite of many C programmers. This statement obeys the syntax

``` for (init-expression; integer-expression; end-expression) statement-or-block [ then statement-or-block ] next-statement ```
In addition to statement-or-block, its specification requires three other expressions. When executed, the `for` statement evaluates init-expression, then it tests integer-expression. If integer-expression evaluates to zero, control passes to next-statement. Otherwise, it executes statement-or-block as long as integer-expression evaluates to a non-zero result. After every execution of statement-or-block, end-expression will get evaluated.

This statement is almost equivalent to

``` init-expression; while (integer-expression) { statement-or-block end-expression; } ```
The reason that they are not fully equivalent involves what happens when statement-or-block contains a `continue` statement.

Despite the apparent complexity of the `for` statement, it is very easy to use. As an example, consider

``` s = 0; for (i = 1; i <= 10; i++) s += i; ```
which computes the sum of the first 10 integers.

### loop

The `loop` statement simply executes a block of code a fixed number of times. It follows the syntax

``` loop (integer-expression) statement-or-block [ then statement-or-block ] next-statement ```
If the integer-expression evaluates to a positive integer, statement-or-block will get executed that many times. Otherwise, control will pass to next-statement.

For example,

``` loop (10) newline (); ```
will execute the `newline` function 10 times.

### _for

Like `loop`, the `_for` statement simply executes a block of code a fixed number times. Unlike the `loop` statement, the `_for` loop is useful in situations where the loop index is needed. It obeys the syntax

``` _for loop-variable (first-value, last-value, increment) block [ then statement-or-block ] next-statement ```
Each time through the loop, the loop-variable will take on the successive values dictated by the other parameters. The first time through, the loop-variable will have the value of first-value. The second time its value will be first-value + increment, and so on. The loop will terminate when the value of the loop index exceeds last-value. The current implementation requires the control parameters first-value, last-value, and increment to be integer-valued expressions.

For example, the `_for` statement may be used to compute the sum of the first ten integers:

``` s = 0; _for i (1, 10, 1) s += i; ```

The execution speed of the `_for` loop is more than twice as fast as the more powerful `for` loop making it a better choice for many situations.

### forever

The `forever` statement is similar to the `loop` statement except that it loops forever, or until a `break` or a `return` statement is executed. It obeys the syntax

``` forever statement-or-block [ then statement-or-block ] ```
A trivial example of this statement is
``` n = 10; forever { if (n == 0) break; newline (); n--; } ```

### foreach

The `foreach` statement is used to loop over one or more statements for every element of an object. Most often the object will be a container object such as an array, structure, or associative arrays, but it need not be.

The simple type of `foreach` statement obeys the syntax

``` foreach var (object) statement-or-block [ then statement-or-block ] ```
Here object can be an expression that evaluates to a value. Each time through the loop the variable var will take on a value that depends upon the data type of the object being processed. For container objects, var will take on values of successive members of the object.

A simple example is

``` foreach fruit (["apple", "peach", "pear"]) process_fruit (fruit); ```
This example shows that if the container object is an array, then successive elements of the array are assigned to `fruit` prior to each execution cycle. If the container object is a string, then successive characters of the string are assigned to the variable.

What actually gets assigned to the variable may be controlled via the `using` form of the `foreach` statement. This more complex type of `foreach` statement follows the syntax

``` foreach var ( container-object ) using ( control-list ) statement-or-block ```
The allowed values of control-list will depend upon the type of container object. For associative arrays (``` Assoc_Type```), control-list specifies whether keys, values, or both are used. For example,
``` foreach k (a) using ("keys") { . . } ```
results in the keys of the associative array `a` being successively assigned to `k`. Similarly,
``` foreach v (a) using ("values") { . . } ```
will cause the values to be used. The form
``` foreach k,v (a) using ("keys", "values") { . . } ```
may be used when both keys and values are desired.

Similarly, for linked-lists of structures, one may walk the list via code like

``` foreach s (linked_list) using ("next") { . . } ```
This `foreach` statement is equivalent
``` s = linked_list; while (s != NULL) { . . s = s.next; } ```
Consult the type-specific documentation for a discussion of the `using` control words, if any, appropriate for a given type.

### break, return, and continue

S-Lang also includes the non-local transfer statements `return`, `break`, and `continue`. The `return` statement causes control to return to the calling function while the `break` and `continue` statements are used in the context of loop structures. Consider:

``` define fun () { forever { s1; s2; .. if (condition_1) break; if (condition_2) return; if (condition_3) continue; .. s3; } s4; .. } ```
Here, a function `fun` has been defined that contains a `forever` loop consisting of statements `s1`, `s2`,...,`s3`, and three `if` statements. As long as the expressions `condition_1`, `condition_2`, and `condition_3` evaluate to zero, the statements `s1`, `s2`,...,`s3` will be repeatedly executed. However, if `condition_1` returns a non-zero value, the `break` statement will get executed, and control will pass out of the `forever` loop to the statement immediately following the loop, which in this case is `s4`. Similarly, if `condition_2` returns a non-zero number, the `return` statement will cause control to pass back to the caller of `fun`. Finally, the `continue` statement will cause control to pass back to the start of the loop, skipping the statement `s3` altogether.

### The looping then clause

As mentioned above, all the looping statements support an optional `then` clause. The statements that comprise this clause get executed only when the loop has run to completion and was not prematurely terminated via a `break` statement. As an example, consider the following:

``` count = 0; max_tries = 20; while (count < max_tries) { if (try_something ()) break; count++; % Failed -- try again } if (count == 20) throw RunTimeError, "try_something failed 20 times"; ```
Here, the code makes 20 attempts to perform some task (via the `try_something` function) and if not successful it will throw an exception. Compare the above to an equivalent form that makes use of a `then`-clause for the `loop` statement:
``` max_tries = 20; loop (max_tries) { if (try_something ()) break; % Failed -- try again } then throw RunTimeError, "try_something failed 20 times"; ```
Here, the `then` statement would get executed only if the loop statement has run to completion, i.e., loops 20 times in this case. This only happens if the `try_something` function fails each time through the loop. However, if the `try_something` function succeeds, then the `break` statement will get executed causing the loop to abort prematurely, which would result in the `then` clause not getting executed.

The use of such a construct can also simplify code such as:

``` if (some_condition) { foo_statements; if (another_condition) bar_statements; else fizzle_statements; } else fizzle_statements; ```
In this case the `fizzle_statements` are duplicated making the code ugly and less maintainable. Ideally one would wrap the `fizzle_statements` in a separate function and call it twice. However, this is not always possible or convenient. The duplication can be eliminated by using the `then` form of the `loop` statement:
``` loop (some_condition != 0) { foo_statements; if (another_condition) { bar_statements; break; } } then fizzle_statements; ```
Here, the expression `some_condition != 0` is going to result in either 0 or 1, causing the code to execute 0 or 1 loops. Since the `fizzle_statements` are contained in the `then` clause, they will get executed only when the requested number of loops executes to completion. Executing 0 loops is regarded as successful completion of the loop statement. Hence, when `some_condition` is 0, the `fizzle_statements` will get executed. The `fizzle_statements` will not get executed only when the loop is prematurely terminated, and that will occur when both `some_condition` and `another_condition` are non-zero.

Next Previous Contents