# Slime

<!--  -->
Slime - эзотерический язык программирования нового поколения, имеющий строгую статическую типизацию и динамическую типизацию одновременно. Он содержит все современные языковые средства*: внутриблочные описания, автоопределение типа, кортежи, срезы, классы, перегрузку операций, интерфейсы, обработку исключений, обобщенные классы и подпрограммы, лямбда-выражения, средства параллельного программирования.
*некоторые из перечисленных средств требуют реализации собственного парсера и интерпретатора текстового содержания функций
## Спецификация
### Типы данных
- Массив - последовательный контейнер произвольного доступа, элементами которого могут быть данные любого типа.
Массив имеет заранее определённую длину - целое число (неограниченного диапазона). Модуль длины массива определяет количество содержащихся в нём элементов, а знак - порядок индексации
- массивы положительной длины - индексируются с нуля
```slime
array = ["a", "b", "c"];
print(array[0]);
print(array[1]);
print(array[2]);
```
Вывод:
```bash
abc
```
- массивы отрицательной длины - индексируются также с нуля, но уже в отрицательном направлении
```slime
array = -["a", "b", "c"];
print(array[0]);
print(array[-1]);
print(array[-2]);
```
Вывод:
```bash
abc
```
Обращение к элементу массива за пределами его диапазона возвращает пустой массив, а запись по такому индексу не производит никакого эффекта (такая операция записи также возвращает `[]`).
### Комментарии
Комментарии как в питоне `#`
### Способы определить массив
```slime
# Просто как массив
x = [[], [[], [], []], [[[]]]];
# Числовые литералы
# Определяют массив, длина которого соответствует значению литерала,
# заполненный пустыми массивами
x = 4; # эквивалентно [[], [], [], []]
# Также поддерживаются двоичные, восьмеричные и шестнадцатеричные числовые литералы
bin = 0b001001;
oct = 010; # Это 8
hex = 0xC0FFEE;
# символьные литералы
# Эквивалентны числовым литералам, определяющим соответствующую кодовую точку Unicode
x = 'x'; # эквивалентно 120
# Строковые литералы
# Эквивалентны определению массива символьных литералов,
# составляющих данную строку
str = "Hello Slime!";
# эквивалентно ['H', 'e', 'l', 'l', 'o', ' ', 'S', 'l', 'i', 'm', 'e', '!']
# Строковые и символьные литералы поддерживают escape последовательности,
# аналогично языку Си
str = "\t***\n\t*x*\n\t***"
# Объявление функции
fib = {
@[0] <= 0 || (fib(@[0] - 1) + fib(@[0] - 2))
};
# Объявление функции всегда создаёт массив из двух элементов,
# первый из которых число (на самом деле массив, длина которого равна этому числу),
# равное индексу фрейма стека, в котором объявлена функция,
# а второй - исходный текст тела функции
# Объявление функции fib в глобальном пространстве эквивалентно следующей записи
fib = [0, "\n @[0] <= 0 || (fib(@[0] - 1) + fib(@[0] - 2))\n"];
```
### Операторы
#### Унарные операторы
- `+` — ничего не делает
```slime
simple_x = [1, 2, 3];
added_x = +[1, 2, 3];
if {simple_x == added_x} {
print("simple_x == added_x");
}
```
Вывод:
```
simple_x == added_x
```
- `-` — создаёт массив с таким же числом элементов, но инвертированным знаком длины. Элемент с индексом `i` исходного массива равен элементу с индексом `-i` результирующего
```slime
x = -[1, 2, 3]; # x[0] == 1, x[-1] == 2, x[-2] == 3
```
- `!` - пустой массив превращает в массив, содержащий один пустой массив, любой другой - в пустой массив
```slime
x = ![]; # [[]]
x = ![1, 2, 3] # []
```
#### Бинарные операторы
- Операции сравнения `>`, `<`, `>=`, `<=`, `==`, `!=` - реализуют полное глубокое лексикографическое сравнение. Массив отрицательной длины всегда считается меньше массивов положительной или нулевой длины. Сравнение двух массивов отрицательной длины на `>`, `<`, `>=`, `<=`, `==`, `!=` эквивалентно сравнению обратным им массивов (взятых со знаком `-`) на `<`, `>`, `<=`, `>=`, `==`, `!=` соответственно.
```slime
# (a > b) эквивалентно (-a > -b)
# (a <= b) эквивалентно (-a <= -b)
```
Операции сравнения возвращают `[]` если результат сравнения ложь, и `[[]]`, если истина
- `+` — конкатенирует массивы.
```slime
x = [1, 2, 3] + ["hello", "slime"]; # [1, 2, 3, "hello", "slime"]
y = -[1, 2, 3] + -[4, 5]; # -[1, 2, 3, 4, 5] ?
# Отрицательные массив при конкатенации с положительным
# уменьшает его размер на собственную длину
a = [1, 2, 3] + -["hello", "slime"]; # [1]
b = [1, 2, 3] + -["hello", "slime", "!", "!!!"]; # -["!!!"]
c = [1] + -["hello", "slime", "!", "!!!"]; # -["slime", "!", "!!!"]
# Для конкатенации применимо следующее правило
# -(a + b) == (-b + -a)
p = -[1] + [a, b, c] # -([1] + -[a, b, c]) => [b, c]
q = -[1, 2, 3] + -["hello", "slime"]; # -[1, 2, 3, "hello", "slime"]
```
- `-` — эквивалентен сложению с отрицательным массивом
```slime
# x - y == x + -y
```
- `*` — умножение, конкатенирует первый массив сам с собой столько раз, каков модуль длина второго. Если второй массив отрицательный, от результата берётся отрицание
```slime
x = "Slime!" * 3; # "Slime!Slime!Slime!"
y = "Slime!" * -3; # -"Slime!Slime!Slime!"
z = -"Slime!" * 3; # -"Slime!Slime!Slime!"
t = -"Slime!" * -3; # "Slime!Slime!Slime!"
```
- `/` и `%` деление и остаток от деления. Если длины массивов положительны, то длина первого массива целочисленно делится на длину второго, в результате оператор `/` возвращает первые p элементов первого массива, где p - результат деления, а операция `%` возвращает последние q элементов, где q - остаток от деления. Если один из массивов отрицательный, результат берётся обратный; если оба отрицательные, знаки отбрасываются.
```slime
x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
x / 3; # [1, 2, 3]
x % 3; # [10]
-x / 3; # -[1, 2, 3]
x / -3; # -[1, 2, 3]
-x / -3; # [1, 2, 3]
```
- `&&` и `||` работают примерно как в js.
+ `&&` - возвращает левый операнд если он `[]` и второй операнд в противном случае
+ `||` - возвращает левый операнд если он не`[]` и второй операнд в противном случае
если операция возвращает левый операнд, значение правого не подсчитывается
- Оператор добавления `.` — `a.b` эквивалентно `a + [b]` (TODO: возможно, стоит выбрать другой символ для этого оператора, например, `^`)
- Оператор `,` — выполняет левую часть, затем правую, возвращает результат правой.
- `=` — оператор присваивания. Левый операнд этого оператора обязан быть lvalue (в ином случае операция не производит никаких действий, и возвращает `[]`). Lvalue в Slime являются идентификаторы(включая @), и результат операции взятия элемента по индексу `[]`.
#### Операторы скобок
- `()` - вызов функции. Параметры, перечисленные внутри скобок через запятую, превращаются в массив - входной параметр функции `@`
```slime
fun = {
print(@[0]);
print(@[1]);
print(@[2]);
};
fun("Hello ", "Slime", "!!!")
```
Вывод:
```bash
Hello Slime!!!
```
- `{}` - вызов функции с передачей функционального параметра. Трактует содержимое скобок как тело функции, и передаёт в `@` как функцию, задавая в качестве индекса стека индекс стека вызывающего контекста
```slime
if = { @() && { @() }} # реализация if из стандартной библиотеки
if { 3 > 0 } {
print("3 > 0");
}
```
Вывод:
```bash
3 > 0
```
- `[]` - оператор доступа по индексу. Возвращает элемент массива. Результат этой операции является lvalue.
#### Приоритет операторов
Как в js. Можно пользоваться круглыми скобками для изменения приоритета. Вызов функции фигурными скобками имеет такой же приоритет, как и вызов обычными круглыми.
#### Идентификаторы
Идентификатор в Slime определяется так же, как и в языке Си. Помимо этого существует специальный идентификатор `@`, для доступа ко входным параметрам функции.
### Функции
Функция — массив вида:
`[<frame_index>, <code>, <p_0>, <p_1>, ...]`
- `<frame_index>` — неотрицательное slime-число (массив, длина которого трактуется как число), определяющее индекс кадра стека, в котором была создана функция
- `<code>` — slime-строка, содержащая исходный код функции
- `<p_i>` — необязательные предварительно связанные параметры функции
При попытке вызвать функцию, не удовлетворяющую этому формату, или если `<code>` не является валидным кодом на slime, возвращается `[]`.
#### Программный стек
При вызове функции создаётся новый кадр программного стека, хранящий информацию о локальных переменных. Индекс кадра стека всегда на 1 больше индекса кадра, вызвавшего функцию. Индекс кадра глобального контекста 0.
Каждая выполняемая функция имеет возможность обращаться в текущий, её собственный, кадр; кадр контекста, в котором она была создана; кадр глобального контекста. Если внутри функции происходит обращение к переменной, она ищется в порядке: текущий контекст, контекст создания; глобальный контекст. Если переменная не найдена, она создаётся в текущем кадре.
#### Содержимое функции
Функция — набор строк кода, отделённых символом `;`. Внутри функции можно использовать обращение к идентификаторам (переменным), операторами языка и круглыми скобками.
В текущем кадре функции всегда определён идентификатор `@`, содержащий ещё входные параметры.
При вызове функции через круглые скобки `@` заполняется набором предварительно связанных параметров, и и набором переданных через запятую входных параметров в скобках.
```slime
foo = {
print(@[0]);
print(@[1]);
print(@[2]);
print(@[3]);
print(@[4]);
};
bar = foo."Slime "."is ";
bar("the best ", "language", "in the world!!!");
```
Вывод
```bash
Slime is the best language in the world!!!
```
При вызове через фигурные скобки, предварительно связанные переменные игнорируются, а в `@` попадает новая созданная функция.
```slime
foo = {
@();
};
bar = foo.{ print("Slime is the worst language in the world") };
bar{ print("Slime is the best language in the world!!!") };
```
Вывод
```bash
Slime is the best language in the world!!!
```
Результатом выполнения функции является результат её последней непустой строки.
Так, любая программа на Slime является slime-функцией, в которой `@` инициализирован набором входных параметров, а её результат — результат выполнения всей программы.
## Стандартная библиотека (TODO)
```slime
null = [];
false = [];
true = [[]];
if = { @() && { @() }};
while = # в разработке
...
```