DOCUMENTATION

Документация: справочник по языку GG

Эта статья представляет собой справочник по языку программирования gg, который следует парадигме функционального программирования на основе лямбда-исчисления и написан на F#. Одной из отличительных черт языка gg является краткость, лаконичность и выразительность, а также то, что все выражения записываются в префиксной форме, в которой операторы располагаются слева от аргументов. Язык программированичя gg поддерживает интерактивную среду для выполнения кода на данном языке - repl, в которой мы можем вводить конструкции на gg, и они будут сразу выполняться.

Компиляция и запуск программы

Для создания первой программы на языке gg потребуется текстовый редактор и фреймворк .NET SDK. Необходимо склонировать репозиторий. Для подключения расширения языка gg в VSCode нужно скопировать директорию gg из данного репозитория в директорию ~/.vscode-server/extensions. Для создания проекта используется команда dotnet new console -lang "F#" -o new_project, которой передается тип проекта. В данном случае мы указываем, что создаваемый проект представляет собой консольное приложение. С помощью флага -o указывается название проекта - new_project. После выполнения данной команды в папке new_project будет создан проект с минимальным набором стандартных файлов и папок:

  1. new_project.fproj - главный файл проекта, который описывает его конфигурацию. В него в <ItemGroup> необходимо подключить \src\Main\Main.fsproj;
  2. program.fs - созданный файл с кодом программы на языке F#. В данной папке необходимо создать файл с расширением .gg для дальнейшего написания кода.

Для запуска программы на gg в консоли в папке new_project необходимо ввести команду dotnet run <название созданного файла>.gg.

Синтаксис

В следующей таблице показан синтаксис для языковых конструкций gg. Фигурные скобки {} заключают определяемые пользователем элементы синтаксиса.

Конструкция языка Синтаксис
Блок кода ( {expr} )
var-привязки (var a = {expr} )
if (if {cond} : ( {expr} ) )
if-else (if {cond} : ( {expr1} ) else : ( {expr2} ))
for (for i [start .. finish] : )
while (while {cond} : )

Типы

Базовые типы, определенные в языке gg:

Тип Описание Принимаемые значения
bool Логический true/false
float Вещественный 64-битные числа с плавающей точкой
string Строковый строка символов Unicode в двойных кавычках

Символы комментариев

Однострочные и многострочные комментарии в языке обозначаются с помощью двойных знаков решетки #comment#.

Арифметические операторы

Оператор Описание
+ При использовании в качестве бинарного оператора складывает левую и правую части, а при использовании в качестве унарного обозначает положительное число
- При использовании в качестве бинарного оператора вычитает из левой части правую, а при использовании в качестве унарного обозначает отрицательное число
* При использовании в качестве бинарного оператора перемножает левую и правую части, а при использовании в качестве унарного обозначает положительное число
/ При использовании в качестве бинарного оператора делит левую часть на правую, а при использовании в качестве унарного возвращает обратное число

Логические операторы

Оператор Описание
& Вычисляет логическое значение операции “И”
| Вычисляет логическое значение операции “ИЛИ”

Операторы сравнения и присваивания

Оператор Описание
= Присваивает левой части значение правой
== Возвращает true, если левая часть равна правой, в противном случае - false
< Возвращает true, если левая часть меньше правой, в противном случае - false
> Возвращает true, если левая часть больше правой, в противном случае - false
<= Возвращает true, если левая часть меньше или равна правой, в противном случае - false
=> Возвращает true, если левая часть больше или равна правой, в противном случае - false

Оператор var. Привязка значений

Оператор позволяет определить значение и связать с данными или действиями. После идентификатора через знак равно указываются связываемые с заданным идентификатором данные или действия.

(var id = expression)

Var применяется для выражения привязки одного конкретного имени. Простейшая форма var выражения привязывает имя к значению. В качестве значения, можно использовать как числа/числовые выражения, так и строки.

(var a = 5)

(var b = (+ 5 1))

(var c = "Hello, world!")

Привязанные имена можно использовать после определения, но не в любом месте после var отображения привязки.

# ERROR: #
(print x)
(var x = 10)
# OK: #
(var x = 10)
(print x)

Базовые операторы ввода-вывода

Базовый ввод-вывод включает в себя запись данных в консоль, а также чтение из файла и запись в него. Для записи в консоль используется функция print.

( print number)
(print "string")
( print (expr))

Для записи в файл используется функция write, которая добавляет строки в файл, затем закрывает его. Если указанный файл не существует, то функция создает его, записывает туда нужные строки и закрывает его.

(write "./file_path" "input_text")

Параметр ./file_path - путь к файлу, который находится в текущей директории, input_text - строки, которые добавляются в файл. Для чтения из файла используется функция read, которая открывает заданный файл и считывает построчно всю информацию, содержащуюся в нем, которая сохраняется в строковую переменную и выводится в консоль.

(print(read "./file_path"))

Параметр ./file_path - путь к файлу, который находится в текущей директории.

Условные выражения

1) if Выражение выполняет ветвь кода и вычисляет значение, если заданное логического выражение истинно.

(
    if (boolean_expression) :
        (
            expression_body
        )
)

2) if-else Выражение выполняет ветви кода и вычисляет различные значения в зависимости от заданного логического выражения.

(
    if (boolean_expression) :
        (
            expression_body_1
        )
    else :
        (
            expression_body_2
        )
)

В предыдущем синтаксисе expression_body_1 выполняется, когда логическое выражение принимает значение true, в противном случае - expression_body_2. Типы значений, создаваемых в каждой ветви, должны совпадать.

Циклы

Язык поддерживает использование операторов kill и continue, позволяющих завершать циклы досрочно, либо продолжать их выполнение, пропуская при этом одну итерацию. 1) Выражения for Используются для прохода по циклу над диапазоном значений переменной цикла.

(
    for identifier [start .. finish] :
        (
            expression_body    
        )
)    

Тип идентификатора выводится из типа выражения начала и окончания цикла. Правая граница диапазона не включается. 2) Выражения while Выражение используется для выполнения итеративного цикла, пока значение заданного условия истинно.

(
    while (condition) :
    (
        expression_body
    )
)

Вычисляется condition. Если оно истинно, то выполняется выражение expression_body. Затем condition проверяется заново. Если оно ложно, то цикл завершается на текущей итерации.

Функции

Функции в языке gg имеют имя, параметры, тело. Определяются с помощью ключевого слова func. Могут быть рекурсивными (функции, вызывающие сами себя). Вызвать функцию можно, указав её название и подаваемое через пробел значение (аргумент функции).

(
    func function_name {parameters} :
        (
            function_body
        )
)

Элемент function_name является идентификатором, который представляет функцию. Элемент parametrs состоит из последовательных параметров, разделенных пробелами. Явный тип для каждого параметра не указывается, а компилятор определяет его автоматически из тела функции. Элемент function_body - выражение, представляющее тело функции Оно состоит из нескольких выражений, которые в результате дают итоговое выражение, являющееся возвращающим значением. Простое определение функции выглядит следующим образом:

(
    func func_example {x} :
    (
        if (> x 0) :
        (
            x
        )
        else :
        (
            0
        )
    )
)

(func_example 5)

В данном примере func_example является именем функции, x - аргументом, имеющим тип float. Условие if-else представляет собой тело функции. Возвращаемое значение имеет тип float. В языке gg функции могут возвращать другие функции.

(
    func sum {x1 x2} :
        (
            (var rez = (+ x1 x2))
            rez
        )
)

(
    func multiply {x1 x2} :
        (
            (var rez = (* x1 x2))
            rez 
        )
)
(
    func f {x1 x2 x3} :
        (
           (if (<= x1 0) :
                (print(multiply x1 x2))
            else :
                (print(sum x2 x3))
           )

        )
)

(f 3 4 5)

Здесь функция f принимает 3 параметра. В зависимости от значения первого возвращает одну из функций: sum или multiply. Причем все возвращаемые функции имеют один и тот же тип.