Эта статья представляет собой справочник по языку программирования 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 будет создан проект с минимальным набором стандартных файлов и папок:
new_project.fproj - главный файл проекта, который описывает его конфигурацию. В него в <ItemGroup> необходимо подключить \src\Main\Main.fsproj;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 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. Причем все возвращаемые функции имеют один и тот же тип.