loops

This proposal is intended to be an extension and clarification of the 1.2 loop spec.

general form

Loops have the following form:

IM IN YR <label> [<operation> [YR] <variable> [FROM <expression>]] [TIL|WILE <expression>]
  <code block>
IM OUTTA YR <label>

terminology

An in-scope identifier is an identifier that has been declared and is accessible within the current scope of the execution.

A containing loop is a loop that contains the statement that is being discussed. That is, the statement falls between the loop start and loop end.

initialization & termination

A loop is initialized with the syntax IM IN YR <label> and is terminated with IM OUTTA YR <label>.

The loop initialization and termination statements surround any number of statements (including zero). The statements are executed from the beginning of the loop until the end of the loop is reached or the execution breaks out of the loop.

When execution reaches the end of a loop (OUTTA), it begins again at the start of the loop, repeating indefinitely until the escape condition is met or the execution breaks out.

nested loops

Loops can be nested within each other, like so:

IM IN YR LOOP
   ...
   IM IN YR INNER_LOOP
      ...
   IM OUTTA YR INNER_LOOP
   ...
IM OUTTA YR LOOP

It is not possible to close an outer loop without first closing an inner loop. This is not legal:

IM IN YR LOOP
   ...
   IM IN YR INNER_LOOP
      ...
IM OUTTA YR LOOP

labels

The labels must be valid LOLCODE identifiers and the beginning and ending label must match. They have a scope that mirrors the scoping rules for variables. They may not be the same as an existing (in-scope) identifier (loop, variable or function). It is not possible to reuse a loop label after the loop has been closed as long as that identifier is in scope, or to reuse a loop label in a nested loop. For example, this is not legal:

IM IN YR LOOP
   ...
IM OUTTA YR LOOP
...
IM IN YR LOOP
   ...
IM OUTTA YR LOOP

This is not legal:

IM IN YR LOOP
   ...
   IM IN YR INNER_LOOP
      ...
      IM IN YR LOOP
         ...
      IM OUTTA YR LOOP
      ...
   IM OUTTA YR INNER_LOOP
   ...
IM OUTTA YR LOOP

breaks & continues

Loop execution can be broken with the command GTFO. This causes execution of the innermost enclosing loop to cease and program execution begins immediately after that loop's terminator (OUTTA).

GTFO can be optionally followed by a valid loop label for any containing loop. Doing so will cause execution of the named loop to cease as well as any nested loops that were currently being executed. The execution continues immediately after the named loop's terminator.

Using GTFO with an out-of-scope identifier is illegal, as is using GTFO with an identifier for a loop that doesn't contain that GTFO.

WHATEVER can be used to stop execution of the statements in the innermost enclosing loop and move execution to the terminating statement of the loop (OUTTA).

loop variable & operation

It is possible to associate a variable and an operation with a loop structure. The variable can be a predeclared variable or it can be declared in the initialization statement by preceding it with YR. A newly declared variable can be used throughout the loop structure, including in the conditional.

The operation must be a unary function, or the keywords WATCHIN, UPPIN or NERFIN. WATCHIN is a special operation, described below.

After the last statement of the loop is executed (or a WHATEVER is reached), the function is evaluated, passing in the current value of the associated loop-variable. The loop-variable is then set to the result of the function and the execution returns to the top of the loop structure to evaluate the conditional.

For this purpose, UPPIN acts as a function that returns SUM OF <value> AN 1, and NERFIN acts as a function that returns DIFF OF <value> AN 1. Note that if the value is not a NUMBR or NUMBAR, this is likely an error.

It is possible to have an associated variable and operation but no conditional.

An associated variable can be given an initial value by using the FROM structure. This is the equivalent of performing a <var> R <from-expression> before the first evaluation at the beginning of the loop. For a new variable, this occurs immediately after the declaration of the variable. A new variable that does not have a FROM value initialization clause has the default initial value. (Likely NOOB.)

With nested loops, it is possible to associate the same variable with each loop, but it will likely give unexpected and/or difficult-to-predict results. Newly declared variables in the outer loop are accessible to any inner loops.

for-each

WATCHIN allows a loop to perform similar to a for-each loop. It has the following structure:

IM IN YR <label> WATCHIN [YR] <loop-variable> FROM <BUKKIT-variable>
  <code block>
IM OUTTA YR <label>

This creates a loop that will iterate over the provided BUKKIT, assigning keys from that BUKKIT to the loop-variable. It continues until the iteration ends (there are no more elements in the BUKKIT) or a GTFO is hit. The keys can be used to get the associated values from the BUKKIT.

The functionality of the loop is slightly different than standard loops, in that the condition (are there any more elements to iterate over?) is evaluated, then the value of the loop variable is updated. This is in contrast to the standard loop where the operation is performed on the variable at the completion of the loop block.

It is assumed that iteration over a BUKKIT will be defined when BUKKITs are defined, and this structure will follow that definition.

variable scope

A newly declared loop variable (with YR) uses the same scoping rules as normal variables. It is effectively declared right before the loop starts and is available to the conditional. It is still accessible after the termination of the loop.

A previously declared variable has the scope of its original declaration. It may be accessible after the termination of a loop.

Variables declared within the loop block are scoped to the block and cease to exist after a single iteration of the loop (after hitting IM OUTTA or WHATEVER). They are not accessible in the conditional or after the termination of a loop. This is currently under discussion.

conditional execution

The initialization statement can contain a conditional expression. There are two types, TIL and WILE.

WILE causes the loop to execute as long as the expression evaluates to WIN (boolean true); that is, while the expression is true.

TIL causes the loop to execute as long as the expression evaluates to FAIL (boolean false); that is, until the condition is true.

In each case, the condition is checked before execution of the enclosed statements. This means that if the condition is met before the loop statements even execute, they will not execute.

If no variable is associated with the loop, an expression that results in a non-boolean result will be cast to a boolean value.

If a variable is associated with the loop an expression that does not result in a boolean result will, as a shorthand, evaluate as an implied BOTH SAEM <loop-var> AN <expression>. In the case of WILE, the loop will execute as long as the loop-var is equal to the value of the expression, and in the case of TIL the loop will execute as long as the loop-var is not equal to the value of the expression.

pseudo-compilation

The normal loop structure would compile to something along the lines of:

<declare variable>               // if there's an associated var and it's new (with YR)
<assign from-value>              // if there's an associated var and a FROM
LOOP-BEGIN:
  <evaluate condition>           // if there is a TIL/WILE
     <condition not met: jump to LOOP-QUIT>
  <perform code block>
      <code hits GTFO: jump to LOOP-QUIT>
      <code hits WHATEVER: jump to LOOP-END>
LOOP-END:
  <perform operation on value>   // if there is an associated var & operation
  <jump to LOOP-BEGIN>
LOOP-QUIT:

And WATCHIN loops would compile to something along the lines of:

<declare variable>   // if the associated var is new (with YR)
LOOP-BEGIN:
  <more elements in BUKKIT?>
     <no more: jump to LOOP-QUIT>
  <assign next element to variable>
  <perform code block>
      <code hits GTFO: jump to LOOP-QUIT>
      <code hits WHATEVER: jump to LOOP-END>
LOOP-END:
  <jump to LOOP-BEGIN>
LOOP-QUIT: