Loops

Looping statements execute the sequence of statements several times until the looping condition becomes false. When the looping condition becomes false, the loop terminates. The looping statements in Jai include while loops and for loops.

while loops

While loops executes a sequence of statements repeatedly until the loop condition is met. Their syntax is while condition action;, where condition is some expression that can be evaluated as true or false, and action is a statement or a block of statements. For instance:

n := 0;
while n < 10 {
    n += 1;
}

This while loop keeps on incrementing the n variable when n is less than 10. When n is no longer less than 10, the program will terminate the loop.

for loops

If you have a type that can be iterated over, such as an array of some sort, you can use a for loop to iterate through it. For-loops in Jai are actually deceptively powerful, for a few reasons.

The simple format for for loops is for set action, where the set is something that supports iteration and action is a statement or a block.

To iterate over a sequence of numbers, say from 1 to 10, simply do:

for number: 1..10 {
  print("Number %\n", number);
}

Here, number is the iterator variable name, but Jai allows you to skip it, in which case it will be called it by default:

for 1..10 {
  print("Number %\n", it);
}

It’s worth noting that this will iterate from 1 to 10 inclusive, or, as mathematicians might put it, [1, 10].

Often, rather than iterating over a sequence of numbers, you’ll want to iterate over an array. Then you simply state the array name as the set. For instance:

my_array := .[5, 10, 15, 20, 25, 30];
for my_array {
    print("We got a %\n", it);
}

In addition to it, Jai also defines it_index by default, which contains the index of the item.

foods := .["Burek", "Pho", "Khachapuri", "Empanadas", "Jjajangmyeon"];
print("Top five dishes:\n");
for foods {
    print(" %. %\n", it_index, it);
}

break statement

The break statement terminates the current loop immediately after the break statement is executed. The break statement works in both for and while loops.

for i: 0..5 {  // This for loop prints out 0, 1, 2 then breaks out of the loop
  if i == 3
    break;
  print("%, ", i);
}

In the example above, the for loop loops three times, printing out 0, 1, 2, then the break statement stops the iteration.

The break statement can also be used to break out of an outer loop through the syntax: break var, where var is the variable name in the for loop. You can only break out of an outer for loop. You cannot break out of an outer while loop.

for i: 0..5 {  
  for j: 0..5 {
    if i == 3
      break i; // breaks out of the outer loop for i: 0..5
    print("(%, %)", i, j);
  }
}

The example above prints out (0,0), (0,1), (0,2), (0,3)… (2,5), then when it reaches i==3, the break statement stops the outer loop.

continue statement

The continue statement is used to skip all the statements in the current loop after the continue statement is executed. The continue statement works in both for and while loops.

for i: 0..5 {  // This for loop prints out 0, 1, 2, 4, 5
  if i == 3
    continue;
  print("%, ", i);
}

In the example above, the for loop loops six times, printing out 0, 1, 2, 4, 5. The value of 3 is not printed since the continue statement causes the program to skip the rest of the loop.

The continue statement can also be used to skip to the outer loop through the syntax: continue var, continue var is the variable name in the for loop. You can only continue out of an outer for loop. You cannot continue out of an outer while loop.

for i: 0..5 {  
  for j: 0..5 {
    if i == 3
      continue i; // breaks out of the outer loop for i: 0..5
    print("(%, %)", i, j);
  }
}

The example above prints out (0,0), (0,1), (0,2), (0,3)… (2,5), (4,0), (4,1), (4,2)…(5,5). The for-loop skips all the value pairs starting with a 3, since the continue skips all the instructions after the continue statement.

remove statement

The remove statement is used to remove an element from a dynamic array […] without needing to rewrite the entire for loop into a while loop. The remove statement assumes an unordered remove, the remove swaps the current element that is being iterated on with the last element, and then removes the last element. The remove statement happens in constant time O(1). remove only works inside of a for loop.

arr: [..] int;
for i: 0..10 {
  array_add(*arr, i);
}
for a: arr {
  if a == 2 {
    remove a;
  }
}

Reverse for loop

To do a for loop in reverse, add a < in front of the for loop. The for loop will start at the beginning number and countdown to the ending number. In this example, the for loop will iterate from 5 down to 0 inclusive.

for < i: 5..0 { // This for loop prints out 5 4 3 2 1 0 in that order
  print("%\n", i); 
}

for loop by pointer

To iterate an array by pointer, add a * in front of the for loop.

array := int.[1, 2, 3, 4, 5];
for * ele : array {
  val := <<ele;
  <<ele = val * val; // take all the values in an array and square the elements
}

for_expansion

Jai allows you to use the for loop to iterate over custom data structures. for loops are designated through a macro. Here is an example of how to create a for loop:

LinkedList :: struct {
  data: int;
  next: *LinkedList;
}

for_expansion :: (list: *LinkedList, body: Code, flags: For_Flags) #expand {
  iter := list;
  i := 0;
  while iter != null {
    `it := iter.data;
    `it_index := i;
    #insert body;
    iter = iter.next;
    i += 1;
  }
}

In the example above, we define a custom LinkedList, a very common computer science data structure. We define using the for loop over that data structure by using a for_expansion. for_expansion takes in three parameters: a pointer to the data structure one wants to use the for loop on, a Code datatype, and a For_Flags flags. The #insert body; inserts body of the for loop at that portion of the macro.

You need to backtick an it and it_index to get the for_expansion working. Else, this is an error.

The For_Flags enum_flags is found in Preload.jai with the following definition:

For_Flags :: enum_flags u32 {
  POINTER :: 0x1; // this for-loop is done by pointer.
  REVERSE :: 0x2; // this for-loop is a reverse for loop.
}

Redefining break and continue inside for_expansion

In the #insert directive, break, continue, and remove can be redefined and the default behavior can be overwritten to do custom things.

#insert (break=do_something(), continue=do_something()) code;
2 Likes