Two for-loop variants are supported: a C-style counting for loop
consisting of an initialization expression, a test condition
and a step expression and a for-in-loop variant which allows
enumerating properties of objects or items of arrays.

Additionally, ucode supports an alternative syntax suitable for
template block tags.


-- Expect stdout --
A simple counting for-loop:
Iteration 0
Iteration 1
Iteration 2
Iteration 3
Iteration 4
Iteration 5
Iteration 6
Iteration 7
Iteration 8
Iteration 9

If the loop body consists of only one statement, the curly braces
may be omitted:
Iteration 0
Iteration 1
Iteration 2
Iteration 3
Iteration 4
Iteration 5
Iteration 6
Iteration 7
Iteration 8
Iteration 9

Any of the init-, test- and increment expressions may be omitted.

Loop without initialization statement:
Iteration null
Iteration 1
Iteration 2

Loop without test statement:
Iteration 0
Iteration 1
Iteration 2

Loop without init-, test- or increment statement:
Iteration 1
Iteration 2
Iteration 3

For-in loop enumerating object properties:
Key: foo
Key: bar

For-in loop enumerating array items:
Item: one
Item: two
Item: three

A counting for-loop using the alternative syntax:
Iteration 0
Iteration 1
Iteration 2
Iteration 3
Iteration 4
Iteration 5
Iteration 6
Iteration 7
Iteration 8
Iteration 9

A for-in loop using the alternative syntax:
Item 123
Item 456
Item 789

For-in and counting for loops may declare variables:
Iteration 0
Iteration 1
Iteration 2

Item 123
Item 456
Item 789
-- End --

-- Testcase --
A simple counting for-loop:
{%
	for (i = 0; i < 10; i++) {
		print("Iteration ");
		print(i);
		print("\n");
	}
%}

If the loop body consists of only one statement, the curly braces
may be omitted:
{%
 	for (i = 0; i < 10; i++)
		print("Iteration ", i, "\n");
%}

Any of the init-, test- and increment expressions may be omitted.

Loop without initialization statement:
{%
	for (; j < 3; j++)
		print("Iteration " + j + "\n");
%}

Loop without test statement:
{%
	for (j = 0;; j++) {
		if (j == 3)
			break;

		print("Iteration ", j, "\n");
	}
%}

Loop without init-, test- or increment statement:
{%
	for (;;) {
		if (k++ == 3)
			break;

		print("Iteration ", k, "\n");
	}
%}

For-in loop enumerating object properties:
{%
	obj = { foo: true, bar: false };
	for (key in obj)
		print("Key: ", key, "\n");
%}

For-in loop enumerating array items:
{%
	arr = [ "one", "two", "three" ];
	for (item in arr)
		print("Item: ", item, "\n");
%}

A counting for-loop using the alternative syntax:
{% for (x = 0; x < 10; x++): -%}
Iteration {{ x }}
{% endfor %}

A for-in loop using the alternative syntax:
{% for (n in [123, 456, 789]): -%}
Item {{ n }}
{% endfor %}

For-in and counting for loops may declare variables:
{% for (let i = 0; i < 3; i++): %}
Iteration {{ i }}
{% endfor %}

{% for (let n in [123, 456, 789]): %}
Item {{ n }}
{% endfor %}
-- End --


By specifying two loop variables in for-in loop expressions, keys
and values can be iterated simultaneously.

-- Expect stdout --
true
false
123
456
[ 0, true ]
[ 1, false ]
[ 2, 123 ]
[ 3, 456 ]
foo
bar
baz
qrx
[ "foo", true ]
[ "bar", false ]
[ "baz", 123 ]
[ "qrx", 456 ]
-- End --

-- Testcase --
{%
	let arr = [ true, false, 123, 456 ];
	let obj = { foo: true, bar: false, baz: 123, qrx: 456 };

	// iterating arrays with one loop variable yields the array values
	for (let x in arr)
		print(x, "\n");

	// iterating arrays with two loop variables yields the array indexes
	// and their corresponding values
	for (let x, y in arr)
		print([x, y], "\n");

	// iterating objects with one loop variable yields the object keys
	for (let x in obj)
		print(x, "\n");

	// iterating objects with two loop variables yields the object keys
	// and their corresponding values
	for (let x, y in obj)
		print([x, y], "\n");
%}
-- End --


Ensure that for-in loop expressions with more than two variables are
rejected.

-- Expect stderr --
Syntax error: Unexpected token
Expecting ';'
In line 2, byte 19:

 `    for (let x, y, z in {})`
  Near here -----------^


-- End --

-- Testcase --
{%
	for (let x, y, z in {})
		;
%}
-- End --


Ensure that assignments in for-in loop expressions are rejected.

-- Expect stderr --
Syntax error: Unexpected token
Expecting ';'
In line 2, byte 20:

 `    for (let x = 1, y in {})`
  Near here ------------^


-- End --

-- Testcase --
{%
	for (let x = 1, y in {})
		;
%}
-- End --


Ensure that too short for-in loop expressions are rejected (1/2).

-- Expect stderr --
Syntax error: Unexpected token
Expecting ';'
In line 2, byte 12:

 `    for (let x)`
  Near here ----^


-- End --

-- Testcase --
{%
	for (let x)
		;
%}
-- End --


Ensure that too short for-in loop expressions are rejected (2/2).

-- Expect stderr --
Syntax error: Unexpected token
Expecting ';'
In line 2, byte 15:

 `    for (let x, y)`
  Near here -------^


-- End --

-- Testcase --
{%
	for (let x, y)
		;
%}
-- End --