Skip to the content.

Операторы и управляющие конструкции

Создания объектов и присвоения новых значений

Для создания объектов и присвоения им новых значений в NewLang используется сразу три разных оператора:

Использование трех разных операторов для создания/изменения объектов позволяет более гибко контролировать подобные операции и выявлять логические ошибки в коде на более раннем этапе. Если же не требуется строго контролировать момент создание объектов и присвоения им значений, можно пользовать есдинственным оператором :=.

    var ::= 1.0; # Создать новую переменную var без явного указания типа
    var = 100; # Присвоить новое значение уже существующей переменной
    printf := :Pointer('printf(format:FmtChar, ...):Int32'); /* Создать новый или переопределить существующий объект printf */

Присваивание значения сразу нескольким переменным и оператор распаковки словаря

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

Причем словарь может быть указан и с левой стороны от оператора присвоения и таким образом можно записать самый простой способ перебора всех его элементов: item, dict := ... dict;, т.е. когда цикле первый элемент словаря сохраняется в переменую item, а из самого словаря удалется.

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

Все операторы имеют парный аналог с присвоением значения:

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

Проверка типа (имени класса объекта):

Для оператора проверки имени класса объекта используется символ тильда ~. Он немного похож на оператор instanceof в Java. Левым операндом должен быть проверяемый объект, а правым — проверяемый тип, который можно указать строкой литералом, переменной строкового типа или именем проверяемого класса непосредственно. Результатом операции будет истина, если правый операнд содержит название класса проверяемого объекта или он присутствует в иерархии наследования.

    name := "class";    # Строковая переменная с именем класса
    var ~ name;         
    var ~ :class;       # Имя типа
    var ~ "class";      # Строка литерал с именем типа

Утиная типизация

Оператор утиной типизации, два символа тильны ~~ — приблизительный аналог функции isinstance() в Python, который для простых типов сравнивает непосредственную совместимость типа левого операнда по отношению к правому. А для словарей и классов в левом операнде проверяется наличие всех имен полей, присутствующих у правого операнда, т.е.:

    (field1=«value», field2=2,) ~~ (); # Истина (т. е. левый операнд словарь)
    (field1=«value», field2=2,) ~~ (field1=_); # Тоже истина (т. к. поле field1 присутствует у левого операнда)
    (field1=«value», field2=2,) ~~ (not_found=_); # Ложь, т.к. поле not_found у левого операнда отсутствует

Строгая утиная типизация ~~~ — для простых типов сравнивается идентичности типов без учета совместимости, а для составных типов происходит сравнение всех свойств с помощью оператора строгого равенства. Для данной операции, пустой тип совместим только с другим пустим типом.

Управляющие конструкции

К управляющим конструкциям языка NewLang относятся условный оператор, два вида циклов, оператор оценки выражения, оператор прерывания последовательности выполнения команд и перехват прерывания. Операторы проверки условий всегда указываются в квадратных скобках, а последовательность команд для выполнения — в фигурных.

Условный оператор

В качестве оператора проверки условия используется синтаксическая конструкция, соответствующая по смыслу термину «следует», т.е. тире и угловая скобка -> или с двумя тире для большей наглядности –>. Такая запись условного оператора очень похожа на математическую и легко объединяется в последовательности для проверки множественных условий вида «else if».

В общем случае условный оператор имеет вид: [ условие ] –> { действие }; или c условием иначе [ условие ] –> { действие }, [_] –> { действие иначе };

Для наглядности записанный с отступами:

    [ условие1 ] -> { действие1 },
        [ условие2 ] -> действие2,
        [ условие3 ] -> действие3,
        [_] -> {действие_иначе};

Оценка выражения

Синтаксическая конструкция с помощью которой реализуется аналог оператора switch выглядит следующим образом:

    [ $var ] ==> {
        [1] -> { code };     # Выполнится проверка условия $var == 1
        [1, 2] -> { code };  # Выполнится проверка условия ($var == 1 || $var == 2)
        [_] -> { code default };  # Ветка условия иначе
    };

Что очень похоже на Pattern Matching, но все же не является сопоставлением с образцом, а скорее более краткая запись множественого оператора сравнения Так как в качестве оператора для оценки могут быть использован любые имеющиеся операторы сравнения на равенство:

Но если в качестве оператора сравнения использовать оператор утиной типизации, то оценка выражения превращается в классический Pattern Matching:

    $value := (f1=1, f2="2",);
    [ $value ] ~~~> {
        [ (f1=_, ), (f1=_, f2=0, ) ] -> { code };     # Поле f2 отсутствует или число 
        [(f1=_, f2="",), (f1=_, f2='',)] -> { code }; # Поле f2 строка
        [_] -> { code default };                      # Код по умолчанию
    };

Операторы циклов

Для указания операторов циклов используются управляющие «-» или «–» между условием цикла, проверкой логического выражения, которое указывается в квадратных скобках и телом цикла. В зависимости от взаимного расположения условия и тела, цикл может быть с предусловием (while) или постусловием (do while):

    [условие while] <<->> {
        тело цикла while
    };

    {
        тело цикла do while
    } <<-->> [условие do while];

Привер реализации цикла foreach для суммирования всех элементов словаря (или одномерного тензора) с использованием оператора раскрытия списка:

    summa := 0;
    dict := (1,2,3,4,5,);
    [ dict ] <<-->> {           # Условие цикла, пока есть данные
        item, dict := ... dict; # Результат оператора распаковка словаря - первый его элемент перемещается в item
        summa += item;          # Вычисление суммы всех элементов словаря
    };

Операторы прерывания выполнения (оператор возврата)

В качестве оператора прерывания/возврата используется два символа минус –. Оператор позволяет прервать выполнение последовательности команд и/или вернуть данные из текущей функции/блока кода и является самым близким аналогом оператора return и throw одновременно. Для того чтобы вернуть данные, их необходимо указать между двумя операторами прерывания, т.е. –100–; # Вернуть указанное значение. Если возвращаемое значение не указано явно, то будет возвращено значение None.

Следование (блок кода/лямбда функция)

Алгоритмическая конструкция, которая отвечает последовательное выполнение нескольких команд/операторов и возвращающая результат выполнения последнего из них. Также, результатом выполнения может быть значение, которое возвращается с помощью оператора прерывания (возврата). Это очень похоже на классическую лямбда функцию, только она выполняется сразу во время определения, а в переменную сохраняется уже результат её выполнения.

Следование без перехвата прерывания оформляется в виде последовательности обычных и фигурных скобок, т.е. (){ run code }; или тоже самое, но сохраняет результата выполнения в переменной: $result := (){ run(); code() };. Но если внутри такой функции будет выполнен оператор прерывания, то она никогда вернет управления и не сохранит возвращаемое значение в переменой $result!

Чтобы перехватывать прерывания, в том числе и возвращаемые значения, необходимо использовать конструкция следования с перехватом прерываний, которая оформляется в виде последовательности обычных и двойных фигурных скобок, т.е. $error := ();. Такая конструкция перехватывает все возвраты и прерывания, если они возникают во время выполнения последовательности команд. В этом случае любой результат будет сохранен в переменной $error как при нормальном завершении, так и в случае возникновения ошибки.

Для более тонкой настройки перехвата прерываний следует использовать типизированную конструкцию, когда в явном виде указывается, какие типы прерываний следует перехватывать. $runtime := ():ErrorRuntime;. Такая конструкция вернет результат только в случае успешного завершения (когда с помощью оператора прерывания возвращается не типизированное значение, например, –“Строка”–;), или при создании прерывания с указанием конкретного типа –:ErrorRuntime(“Описание ошибки”)–;. А вот при возникновении любого другого типизированного прерывания, значение перехвачено не будет и все отработает как самый первый вариант, т.е. без перехвата прерывания и без сохранения возвращаемого значения в переменную.

Стратегия обработки ошибок

Обработка ошибок состоит из комбинации двух элементов: оператора прерывания выполнения с указанием типа возвращаемого значения и алгоритмической конструкции следование с возможностью перехвата прерывания заданного типа.

Это немного отличается от классического варианта обработки исключений, который в обычных языках программирования обычно оформляется ключевыми словами try… catch… finally с различными вариациями. Ведь основная цель подобных синтаксических конструкций — выделить участок кода, где возможно возникновение ошибки, перехватить и обработать правильный тип данных (исключений), т.к. NewLang не делает различий между операторами возврата и генерации исключения.

Подход к обработке исключений следующий:

Программный код, который может привести к ошибке, заключается в двойные фигурные скобки, а результат выполнения такого блока кода присваивается переменной. После этого анализируется возвращенное значение, например, оператором сравнения по образцу:

    $result := (){ {     # начало аналога блока try
        $value := call_or_exception1();
        [условие1] -> { -- :Error -- };
        [условие2] -> { -- $value -- };
        $value := call_or_exception2();   
    }};                 # конец аналога блока try

    [$result] ~> {      # Для сравнения по образцу использовать оператор проверки типа (имени класса)
        [:ErrorParser] -> {Код обработки ошибок парсера};
        [:ErrorRunTime] -> {Код обработки ошибок времени выполнения};
        [:Error] -> { Код обработки остальных ошибок };
        [_] -> { Обработка нормальных данных $value без ошибок };
    };