loop statement - Raesangur/PascalScript_Reference GitHub Wiki
Detailed Syntax
- loop_statement:
(optional) loop_attributes
loop
((optional) loop_init_statement; loop_condition; (optional) loop_iteration_expression): loop_block- or
(optional) loop_attributes
loop
((optional) loop_init_statement; (1 ... n)ranged_loop_declarationin
loop_expression): loop_block
-
do_loop_statement:
(optional) loop_attributes
do loop
((optional) loop_init_statement; loop_condition; (optional) loop_iteration_expression): loop_block
-
loop_init_statement:
- (1 ... n) comma-separated declarations.
- If used, must end in a
;
. If unused,;
may be skipped. - Will be executed once before the beginning of the loop.
- Variables declared in the loop_init_statement are in the same scope as variables declared in the loop_block.
-
loop_condition:
- A boolean-resulting expression.
- In a regular
loop
loop, this expression is evaluated at the beginning of every iteration of the loop, including the first one. - In a
do loop
loop, this expression is evaluated at the end of every iteration of the loop.
-
loop_iteration_expression:
- (1 ... n) comma-separated expressions to be executed at the end of the loop_block.
- If unused, the preceding
;
may be skipped.
-
loop_block:
(optional) statement
-
ranged_loop_declaration:
- A declaration of a variable of type
ranged_loop_expression.begin().types[0]
orranged_loop_expression.begin().types[0]&
. - The variable declared in the ranged_loop_declaration will be updated before each iteration of the loop.
- If multiple ranged_loop_declaration and ranged_loop_expression pairs are provided (separated by commas), both variables will be updated from their respective ranged_loop_expression before each iteration of the loop.
- A declaration of a variable of type
-
ranged_loop_expression:
- An expression which resulting value has a
begin()
andend()
function defined, that both return an object respecting theiterator
type traitdefine rle = ranged_loop_expression, rle.has_member(begin()) && rle.has_member(end()) && std.type_trait.is_iterator(rle.begin().returns()[0])
- An expression which resulting value has a
loop(int i = 0; i < 6; i++): // Regular C-style for loop
std.io.println(i);
loop(int i in [0 ... 9]): // Ranged-based loop, using the `loop.index` variable
{
std.io.println(i);
if(loop.index == 5):
break;
}
int i = 6;
loop(i): // Loop with only condition expression filled in
{
std.io.println(6 - i--);
}
loop(int[] array = [0 ... 9], // Two initialized values & two iteration expressions
int* ptr = array.begin();
ptr < array.end(); *ptr = 0, ptr++): // Will set the value in the array to 0 before moving the iterator
{
std.io.println(*ptr);
if(loop.index == 5):
break;
}
do loop(int i = 0; i < 5; i++):
std.io.println(i);
All output:
0
1
2
3
4
5
A special flavor of the loop
loop is the do_loop
loop, in which the condition is evaluated at the end of the loop_block.
loop
statements can also be used to loop over an iterating range of values, such as an array, a string literal or any standard library containers.
The keyword in
is used to denote the ranged_loop_declaration from the ranged_loop_expression.
These ranged-based loop
loops need the iterating object to have the begin()
and end()
methods defined, that both return an object compatible with the iterator
requirements from type_traits
.
If multiple ranged_loop_declaration and ranged_loop_expression pairs are provided, each loop variable will be updated from its expression at the beginning of the loop until the first expression reaches its end, effectively acting as a zipping loop.
These range-based loop
loops will simply expand in a regular loop
loop, where syntax (1) is equivalent to syntax (2).
// Syntax (1)
loop(int i in [0 ... 9]):
{
std.io.println(i);
}
// Syntax (2)
// ranged_loop_expression was of type int[]&&;
// int[].begin() and int[].end() returns an int*
loop(int[] expression = [0 ... 9],
int* it = expression.begin();
it < expression.end();
++it): // If ++operator() is not available for iterator type, will try with operator++()
{
int i = *it;
// ranged-based loop statements begin here:
std.io.println(i);
}
// Syntax (1)
loop(int a in [0 ... 9], int b in [10 ... 15])
{
std.io.println(a + b);
}
// Syntax(2)
loop(int[] expression1 = [0 ... 9],
int* it1 = expression1.begin()
int[] expression2 = [10 ... 15],
int* it2 = expression2.begin();
it1 < expression1.end() && it2 < expression2.end()>;
++it1, ++it2):
{
int a = *it1;
int b = *it2;
// ranged-based loop statements begin here:
std.io.println(a + b);
}
The loop.index
variable is an easy way to keep track of the loop index during ranged-based loop
loops (or any non-index-based loop
loops).
This variable is normally not included in the compiled code, but if used, will be included to keep track of the current loop index.
For range-based loop
loops, the loop.index
variable will be calculated from the begin position and the loop iterator (see example (1)).
For other loops, there are two possibilities:
- If the loop
loop contains a simple loop(type i = value; i < value; i++)
, and someType.has_member(operator-(value.type)) == true
, the loop.index
variable will be calculated from the used loop index i
as long as this index is not modified during the loop execution (see example(2)).
- If the loop
loop is more complicated, has an index that can't be easily calculated from or modifies the index during execution, the loop.index
variable will be calculated independently (see example(3)).
// EXAMPLE (1)
// Syntax (1)
std.io.println("\nEXAMPLE 1 - Syntax 1");
loop(int i in [0 ... 5]):
{
std.io.print(loop.index + i);
}
// Syntax (2) <- Will be generated from Syntax (1)
std.io.println("\nEXAMPLE 1 - Syntax 2");
loop(int[] expression = [0 ... 5],
int* begin = expression.begin(),
int* it = begin;
it < expression.end(); ++it):
{
int i = *it;
std.io.print((it - begin) + i); // loop.index is simply substituted
}
// EXAMPLE (2)
// Syntax (1)
std.io.println("\n\nEXAMPLE 2 - Syntax 1");
loop(int i = 5; i < 10; i++):
{
std.io.print(i - loop.index);
}
// Syntax (2) <- Will be generated from Syntax (1)
std.io.println("\nEXAMPLE 2 - Syntax 2");
loop(int begin = 5, int i = 5; i < 10; i++):
{
std.io.print(i - (i - begin));
}
// EXAMPLE (3)
// Syntax (1)
std.io.println("\n\nEXAMPLE 3 - Syntax 1");
loop(int i = 0; i < 5; i++):
{
std.io.print(i + loop.index);
if (loop.index == 3):
i = 5;
}
// Syntax (2) <- Will be generated from Syntax (1)
// loop indexing variable is modified during execution, a standalone index will be generated
std.io.println("\nEXAMPLE 3 - Syntax 2");
loop(int index = 0, int i = 0; i < 6; index++, i++):
{
std.io.print(i + index);
if(index == 3):
i = 4;
}
Output:
EXAMPLE 1 - Syntax 1
0246810
EXAMPLE 1 - Syntax 2
0246810
EXAMPLE 2 - Syntax 1
55555
EXAMPLE 2 - Syntax 2
55555
EXAMPLE 3 - Syntax 1
02469
EXAMPLE 3 - Syntax 2
02469
The #infinite
attribute allows for better compiler optimization and warnings. A loop
with the loop_condition evaluating to a compile-time true
will emit an infinite loop warning.
Adding the #infinite
attribute suppresses the warning and can allow for better optimization of the loop.
This attribute can also be used to suppress watchdog conditions if supported by the implementation.
If the loop's condition doesn't evaluate to true
but the #infinite
attribute is present, a warning will also be emitted.
#infinite
loop(true): // Would emit warning if it weren't for the #infinite attribute
std.io.println("Good morning");
an #infinite
loop can also be written shorthand like this:
#infinite
loop():
std.io.println("Good morning");
The #parallel
attribute is an optimization hint to run the loop in parallel on as many threads as specified if possible, modulo the numbers of iterations (if known at compile-time). Leaving this field empty (with or without parentheses) will take as many threads as recommended for the targeted platform. Other hardware acceleration tricks such as SIMD instruction usage may also occur from this optimization hint.
int[10] values;
#parallel(4)
loop(int i in [0 ... 10]):
values[i] = i * 2;