Справочник по синтаксису Ray
============================

`Ray` на самом деле является языком программирования `Noct` с дополнительными API Suika3.

## Присваивания

Переменные в Noct имеют динамическую типизацию и не требуют явного
объявления. Оператор присваивания (`=`) используется для создания
переменных и присваивания им значений.

Как показано в примере ниже, Noct поддерживает различные типы данных,
включая целые числа, числа с плавающей точкой и строки. Переменным можно
повторно присваивать значения разных типов в любой момент выполнения.

```
func main() {
    var a = 123;
    print(a);

    var b = 1.0;
    print(b);

    var c = "string";
    print(c);
}
```

## Глобальные переменные

Глобальные переменные можно определять в функциях, но нельзя определять
вне функций.

```
func main() {
    globalVariable = 123;
    print(globalVariable);
}
```

## Локальные переменные

Ключевое слово `var` позволяет объявить переменную как локальную. Без
объявления `var` присваивание переменной создает глобальную переменную.

```
func main() {
    var a = 123;
    print(a);
}
```

## Массив

Массивы - это упорядоченные коллекции значений, доступных по индексу.
Массивы поддерживают итерацию через конструкцию цикла `for`, позволяя
перебирать каждое значение напрямую.

```
func main() {
    var array = [0, 1, 2];
    for (value in array) {
        print(value);
    }
}
```

Массивы могут одновременно содержать значения разных типов, что отражает
динамическую систему типов.

```
func main() {
    var array = [123, "string"];
}
```

Язык предоставляет встроенную функцию `push()` для добавления элементов в
конец массива. Кроме того, `pop()` удаляет последний элемент.

```
func main() {
    var array = []
    array->push(0);
    array->push(1);
    array->push(2);

    var last = array->pop();

    print("Длина = " + array.length);
}
```

## Словарь

Словари хранят пары ключ-значение, подобно хеш-таблицам или объектам в
других языках. Они задаются фигурными скобками с парами ключ-значение,
разделенными двоеточиями. Словари поддерживают итерацию, при которой
одновременно доступны и ключ, и значение.

```
func main() {
    var dict = {key1: "value1", key2: "value2"};
    for (key, value in dict) {
        print("ключ = " + key);
        print("значение = " + value);
    }

    print("Длина = " + dict.length);
}
```

Словари можно строить пошагово. Присваивание может использовать стиль
массива с `[]` или объектный стиль с `.`.

```
func main() {
    var dict = {};
    dict["key1"] = "value1";
    dict.key2 = "value2";
}
```

Встроенная функция `remove()` позволяет удалять записи по ключу.

```
func main() {
    var dict = {key1: "value1", key2: "value2"};
    remove(dict, "key1");
}
```

## Цикл for

Конструкция цикла for предоставляет краткий синтаксис для перебора
последовательностей, таких как диапазоны, массивы и словари.

Синтаксис диапазона (с оператором `..`) создает итератор, который
генерирует значения от начального значения до значения на единицу меньше
конечного.

```
func main() {
    for (i in 0..10) {
        print(i);
    }
}
```

Циклы for также могут напрямую перебирать массивы и другие типы коллекций.

Массивы можно перебирать с помощью синтаксиса for-value.

```
func main() {
    array = [0, 1, 2];
    for (value in array) {
        print(value);
    }
}
```

Словари можно перебирать с помощью синтаксиса for-key-value.

```
func main() {
    var dict = {key1: "value1", key2: "value2"};
    for (key, value in dict) {
        print(key + "=" + value);
    }
}
```

## Циклы while

Цикл while предоставляет традиционный механизм итерации, который
продолжает выполнение, пока указанное условие остается истинным. В отличие
от циклов for, предназначенных для перебора коллекций, циклы while более
гибкие и могут использоваться для реализации различных алгоритмов, где
число итераций заранее неизвестно. В примере показана базовая реализация
счетчика, увеличивающегося от 0 до 9.

```
func main() {
    var i = 0;
    while (i < 10) {
        print(i);
        i = i + 1;
    }
}
```

## Блоки if и else

Потоки управления позволяют выполнять условное выполнение на основе
вычисленных выражений. Конструкция if-else использует привычный синтаксис,
в котором условия проверяются последовательно.

```
func main() {
    var a = readint();
    if (a == 0) {
        print("0");
    } else if (a == 1) {
        print("1");
    } else {
        print("другое");
    }
}
```

## Лямбда-функции

Функции являются объектами первого класса в языке. Анонимные функции,
также известные как выражения `lambda`, позволяют создавать функции без
имен.

```
func main() {
    var f = (a, b) => { return a + b; }
    print(f(1, 2));
}
```

В процессе компиляции лямбда-функции просто преобразуются в именованные
функции. Поэтому они не могут захватывать переменные, объявленные во
внешних функциях.

## Инкремент/декремент (+=, -=, ++, --)

```
func main() {
    var a = 123;
    a += 321;
    a++;

    var b = 123;
    b -= 321;
    b--;
}
```

`++` и `--` поддерживаются только как самостоятельные инструкции (`a++;`, `b--;`).
Использовать их внутри выражений запрещено, чтобы избежать сложных побочных эффектов.

## ООП в Noct

Объектно-ориентированная модель в Noct - это облегченный вариант ООП на основе прототипов.

- Классы являются просто шаблонами словарей
- Наследование и создание экземпляров реализуются слиянием словарей
- Цепочки прототипов нет, и изменение класса не влияет на существующие экземпляры

Эта модель рассматривает словари как объекты первого класса, а автор называет ее Dictionary-based OOP (D-OOP).

```
func main() {
    // Определение базового класса. (Класс - это просто словарь.)
    Animal = class {
        name: "Animal",
        cry: (this) => {
        }
    };

    // Определение подкласса. (Просто слияние словарей.)
    Cat = extend Animal {
        name: "Cat",
        voice: "meow",
        cry: (this) => {
            print(this.name + " издает звук " + this.voice);
        }
    };

    // Создание экземпляра. (Просто слияние словарей.)
    var myCat = new Cat {
        voice: "neee"
    };

    // Вызов this использует синтаксис -> (). (Эквивалентно myCat.cry(myCat))
    myCat->cry();
}
```

---

## Встроенные функции

### int()

```
var i = int(1.23);
```

### float()

```
var f = float(123);
```

### newArray()

```
var array = newArray(10);
```

### push()

```
var array = [1, 2, 3];
array->push(4);
```

### pop()

```
var array = [1, 2, 3];
var last = array->pop();
```

### resize()

```
var array = [1, 2, 3];
array->resize(2);
```

### charCount()

```
var s = "ABC文あいう";
var l = s->charCount();
```

### charAt()

```
var s = "ABC文あいう";
for (i in 0 .. s.length) {
   var c = s->charAt(i);
   print(c);
}
```

### substring()

```
var s1 = "ABCDEFG";
var s2 = s1.substring(0, 3); // от символа 0, три символа
var s3 = s1.substring(2, -1); // от символа 1 до конца
}
```

### unset()

```
var dic = {key1: "ABC"};
dic->unset("key1");
```

---

## Math API

## Math

### abs()

```
var a = abs(x);
```

### random()

```
var r = random(); // 0 .. 1.0
```

### Math.sin()

```
var y = sin(x);
```

### Math.cos()

```
var y = cos(x);
```

### Math.tan()

```
var y = tan(x);
```
