Created Loop statement/expression (markdown)

Bartłomiej Przemysław Pluta
2020-03-22 15:58:13 +01:00
parent 338bccb09d
commit 73912e6e30

@@ -0,0 +1,242 @@
SMNP language provides a loop statement which also acts as expression.
The statement has been totally designed from scratch and is not similar
to any known loop statements from other languages at all.
Loop allows you to repeat some instructions with counter set programmatically.
All loops are created through loop operator (`^`).
# Simple loop
The simplest loop consists only of counter, loop operator and instruction or block
of instructions.
Example:
```
# the simplest loop
3 ^ print("Money "); # Money Money Money
# the same using block of code
3 ^ {
print("Money ");
}
# Output: Money Money Money
```
Of course, you can use any expression as counter, like variable or function call:
```
function provideCounter(x: int) {
return x * 2;
}
provideCounter(5) ^ print("a"); # aaaaaaaaaa
```
You can define a counter variable whose value is increased with each iteration
using `as` clause (note, that values start from 0):
```
3 as i ^ print(i, " "); # 0 1 2
```
# *while* loop
You can create a loop which is controlled by logic condition.
It can be done just putting a `bool` value as a counter.
Example:
```
end = false;
i = 0;
not end ^ {
if (i == 3) {
end = true;
}
i = i + 1;
}
println(end); # true
println(i); # 4
```
Note that condition-controlled loop doesn't support `as` clause.
# *For-each* loop
This kind of loop works with lists, maps and strings.
## List
You can put a list as a counter. In this case statement will be executed
for each element of list. You can obtain access to element through `as` clause
like in previous examples.
Example:
```
myList = [@c, @d, @e];
myList ^ print("Money "); # Money Money Money
myList as item ^ print(item, " "); # C D E
```
You can put additional parameter into `as` clause. In this case the first parameter
will be the value of passed list and the second one will be iterator that is increased after each loop iteration. If you are using more than one parameter in `as` clause,
you have to enclose them between parentheses.
Example:
```
[@c, @d, @e] as (n, i) ^ println(i, ". ", n);
# Output:
# 0. C
# 1. D
# 2. E
```
## Map
Map is also supported to act as a counter of loop.
In this case statement will be executed for each value of passed map.
Example:
```
x = {
a -> @c,
b -> @d,
c -> @e
};
x ^ print("Money "); # Money Money Money
```
You are also able to access keys and values of iterated map using parameters in `as` clause.
You can use up to three parameters, where
* the first is just a value
* the second is a corresponding key
* the third is an iterator increased by one with each iteration.
Example:
```
myMap = {
first -> true,
second -> [@c, @d, @e],
third -> 14
};
myMap as value ^ println(value);
# Output:
# true
# [C, D, E]
# 14
myMap as (value, key) ^ println(key, ": ", value);
# Output:
# first: true,
# second: [C, D, E]
# third: 14
myMap as (value, key, i) ^ println(i, ". ", key, ": ", value);
# Output:
# 0. first: true,
# 1. second: [C, D, E]
# 2. third: 14
```
## String
Loop operator supports also `string` counter. In this case, you are able to iterate through each character
of passed string. As in the previous cases, you are able to access both current character and iterator using `as` clause.
Example:
```
text = "Hello";
text ^ print("0"); # 00000
text as char ^ println(char);
# H
# e
# l
# l
# o
text as (char, index) ^ println(index, ". ", char);
# 0. H
# 1. e
# 2. l
# 3. l
# 4. o
```
# Filters
Loops in SMNP language also support filtering.
The filtering can be done with `%` clause, which contains a condition
that must be met to execute iteration. `%` clause is placed at the very last
of loop statement.
Example:
```
10 as i ^ println(i) % mod(i, 2) == 0;
# Output:
# 0
# 2
# 4
# 6
# 8
```
Filtering can be useful with simple counting loops like above, but it is really powerful with
*for-each* loops. You can pass further only elements of list/map that met
defined conditions.
It is even more useful with special feature of loops that is covered in next section.
# Loop expression
The power of designed loops relies on the fact, that all kinds of them actually can work as **expressions**.
It means that you can use any kind of loop as e.g. function argument, because they can produce a value.
They work similar to Python's list comprehension and actually produce a new list.
One important condition you have to met in order to use loop as expression is to put
expression as a loop's statement. Because of that in loop expression you can't use
block of code. The statement must be a single instruction - expression.
Example:
```
x = [1, 2, 3, 4];
# 'y' holds numbers multipied by 2
y = x as i ^ i * 2; # y = [2, 4, 6, 8]
# 'z' holds squares of even numbers
z = x as i ^ i ** 2 % mod(i, 2) == 0; # y = [4.0, 16.0]
```
Note, that loop statement is right associative.
That means nested loop statements are being accumulated in right-hand branch.
Take look at example:
```
2 ^ 4 ^ 6 ^ print("a");
# is exactly the same as
2 ^ (4 ^ (6 ^ print("a")));
```
is parsed to following abstract syntax tree:
```
^
/ \
2 ^
/ \
4 ^
/ \
6 print("a")
```
It could be a flaw if you would like to create a mapping chain using loop statements
(like streams in Java 8 or LINQ in .NET), because it requires from operator
to be left associative, so that first instruction would produce value and pass it further.
It can be of course achieved with parentheses:
```
data = ["lorem", "ipsum", "dolor", "sit", "amet"];
output = (((((data as d
^ d
% d.length > 3) as d
^ d.length) as d
^ d * 2) as d
^ d + 1) as d
^ d
% d == 11);
println(output); # [11, 11, 11]
```
As you can see it actually works but is not convenient and readable.