Операторы

Операторы задают действия.

Оператор
    : Локальное-описание
    | Простой-оператор
    | Оператор-если
    | Оператор-надо
    | Оператор-выбор
    | Оператор-пока
    | Оператор-цикл
    | Оператор-вернуть
    | Оператор-прервать
    | Оператор-авария

Простой-оператор
    : Оператор-выражение 
    | Оператор-присваивания
    | Инкремент 
    | Декремент

Блоки

Операторы сгрупированы в Блоки, которые задают последовательность действий для функций и методов и входа в модуль. Блок – это, возможно, пустая последовательность операторов, которые могут включать локальные описания.

Блок: '{' Список-операторов? '}'
Список-операторов: Оператор (Разделитель Оператор)* 

Локальные описания

Локальное описание определяет идентификатор, для которого областью действия является часть блока, от точки завершения описания до завершающей скобки блока, исключая вложенные блоки, в которых описан такой же идентификатор.

Локальное-описание: Описание-переменной

Для локальных переменных не может быть задана поздняя инициализация.

Нужны ли локальные константы? Или это задача оптимизации?

Выражение, как оператор

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

Оператор-выражение: Выражение
напечатать("Привет")
Факториал(5)

Не могут быть использованы в качестве оператора

1 + 1
длина(а)

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

Присваивание заменяет текущее значение, хранящееся в переменной, новым значением, заданным выражением.

Оператор-присваивания: Выражение ':=' Выражение

Выражение в левой части должно быть изменяемым:

Левое выражение Пример Условие
Имя объекта пер или мод.пер пер – это изменяемая переменная
Доступ к полю что-то.поле что-то – это изменяемое выражение, поле – это изменяемое поле
Индексация что-то[индекс] что-то – это изменяемое выражение
Преобразование что-то(:Тип) что-то – это изменяемое выражение

Выражение в правой части должно быть совместимо по присваиванию с типом левого выражения.

пусть ц := 0
пусть байты := Байты[1, 2, 3]
ц := 2
байты[ц] = 5

Ошибка присваивания в неизменяемое выражение

пусть ц = 0
ц := 2

Инкремент и декремент

Операторы Инкремент (++) и Декремент (--) увеличивают или уменьшают свои операнды на 1.

Инкремент: Выражение '++'
Декремент: Выражение '--'

Как и в случае присваивания, выражение должно быть изменяемым. Тип выражения должен быть одного из типов: Байт, Цел64, Слово64.

пусть ц := 1
пусть байты := Байты[1, 2, 3]
байты[ц]++

Оператор если

Оператор если определяет условное выполнение двух ветвей в соответствии со значением логического выражения. Если выражение принимает значение истина, выполняется Блок первой ветви, в противном случае, выполняется ветвь иначе, если она задана.

Оператор-если: 
    'если' Выражение Блок ('иначе' (Оператор-если | Блок))?
если ц > макс { ц := макс }

если Буква?(сим) { Имя() }
иначе если Цифра?(сим) { Число() }
иначе { Ошибка!() }

Оператор надо

Оператор надо используется для завершения исполнения операторов некоторого контекста, если условие, заданое логическим выражением, не выполнено.

Оператор-надо: 
    'надо' Выражение 'иначе' (Завершающий-оператор | Блок)
Завершающий-оператор
    : Оператор-вернуть
    | Оператор-прервать
    | Оператор-авария

Если в ветви иначе стоит Блок, то последним оператором блока должен быть завершающий оператор.

Завершающий оператор Действие
вернуть выход из тела функции, метода или входа
прервать выход из ближайшего объемлющего цикла
авария аварийное завершение программы
надо делитель # 0 иначе авария("деление на ноль")

надо число > 1 иначе вернуть 1

Оператор выбор

Оператор выбор выбирает одну ветвь выполнения из нескольких вариантов.

Оператор-выбор
    : Выбор-по-выражению 
    | Выбор-по-предикатам
    | Выбор-по-типу

Есть три разновидности оператора выбора

Выбор по выражению

В операторе выбор по выражению выбор идет по по значению выражения оператора (значение сравнивается со значениями выражений в вариантах).

Выбор-по-выражению: 
    'выбор' Выражение '{'
    Вариант*
    ('другое' Список-операторов?)?
    '}'
Вариант:
    'когда' Выражение (',' Выражение)* ':' 
    Список-операторов?

При выполнении вычисляется Выражение оператора, затем выражения в Вариантах вычисляются слева направо и сверху вниз. Как только значение выражения Варианта стало равным значению выражения оператора, выполняется список операторов этого Варианта. Остальные варинты игнорируются. Если ни один из вариантов не выполнен, и есть ветка другое, выполняется список операторов этой ветки.

Тип каждого выражения в вариантах должен быть равен эквивалентен типу первого выражения.

выбор х {
когда 0: вернуть "ничего"
когда 1: вернуть "один"
когда 2: вернуть "два"
другое вернуть "много"
}

Выбор по предикатам

Если выражение после ключевого слова выбор отсутствует, то это оператор выбора по предикатам.

Выбор-по-выражению: 
    'выбор' '{'
    Вариант*
    ('другое' Список-операторов?)?
    '}'
Вариант:
    'когда' Выражение (',' Выражение)* ':' 
    Список-операторов?

В этом случае, каждое выражение в Вариантах должно быть выражением логического типа. Эти выражения вычисляются слева направо и сверху вниз до тех пор, когда значение выражения не равно истине. Как только значение выражения стало рано истине, выполняется список операторов этого варианта. Остальные варинты игнорируются. Если ни один из вариантов не выполнен, и есть ветка другое, выполняется список операторов этой ветки.

выбор х {
когда х < 0: вернуть "отрицательное"
когда х > 0: вернуть "положительное"
другое вернуть "ноль"
}

Выбор по типу

В операторе выбор по типу выбор ветви выполнения происходит по типу выражения.

Выбор-по-выражению: 
    'выбор' Переменная-варианта? 'тип' Выражение '{'
    Вариант-типа*
    ('другое' Список-операторов?)?
    '}'
Переменная-варианта:    
    'пусть' Идентификатор ':'
Вариант-типа:
    'когда' Указ-типа (',' Указ-типа)* ':' 
    Список-операторов?

Рассмотрим сначала оператор, в котором переменная варианта не указана.

При выполнении такого оператора вычисляется Выражение, тип которого должен быть типом класса. Для дальнейшего сравнения используется тип объекта, то есть тип, с которым объект был создан, иначе говоря, динамический тип.

В следующем примере для сравнения будет использовать тип К2, так как это тип объекта к:

тип К1 = класс {}
тип К2 = класс (К1) {}
вход{
    пусть к: К = К2{}
    выбор тип к { ... }
}

Динамический тип выражения сравнивается слева направо и сверху вниз на равенство типам, указанным в вариантах. Как только один из типов Варианта оказался равен типу выражения, выполняется список операторов этого Варианта. Остальные варинты игнорируются.

Если ни один из вариантов не выполнен, и есть ветка другое, выполняется список операторов этой ветки.

Сравнение типа выражение оператора выполняется на строгое равентсво, наследование не учитывается. Таким образом, в примере сработает ветка когда К2, независимо от порядка веток в операторе.

тип К1 = класс {}
тип К2 = класс (К1) {}
вход{
    пусть к: К = К2{}
    выбор тип к { 
    когда К1: 
    когда К2: // сработает эта ветка
    }
}

Если в операторе указана переменная варианта, то в каждой ветки исполнения (но не в ветке другое) будет доступна переменная с именем, заданным идентификатором, и типом, указанном в варианте. При этом в варианте должен быть указан только один тип.

тип Человек = класс { имя := "" }
тип Работник = класс (Человек) {
    зарплата := 0.0
}
фн зарплата(чел: Человек): Вещ64 {
    выбор пусть х: тип чел { 
    когда Работник: вернуть х.зарплата
    другое вернуть 0.0
    }
}

Переменная варианта является переменной с единственным присваиванием, её значение изменить нельзя.

Оператор пока

Оператор пока определяет повторное выполнение блока до тех пор, пока значение логического выражение равно истина. Выражение вычисляется перед каждой итерацией.

Оператор-пока: 'пока' Выражение Блок 
пока а < б {
    а := а * 2
}

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

{stmt:cycle-stmt}}

Оператор цикл определяет выполнение блока для всех элементов вектора или другого индексируемого объекта.

Оператор-цикл: 
    'цикл'  ПеременныеЦикла 'среди' Выражение Блок 
ПеременныеЦикла
    : ПеременнаяЦикла
    | '[' ПеременнаяЦикла ']' ПеременнаяЦикла?
ПеременнаяЦикла: Идентификатор    

Выражение цикла должно быть индексируемым, то есть

  • тип выражения должен быть типом вектора
  • или выражение должно обозначать вариативный параметр.

Оператор позволяет задать две переменные цикла или только одну из них. В теле цикла, значением переменной индекса, заданной в квадратных скобках, является текущий индекс (от 0 до длины-1), а переменной элемента - значение элемента по этому индексу.

Типы переменных цикла задаются неявно:

  • тип переменной индекса есть Цел64
  • тип переменной элемента равен типу элемента индексируемого выражения

Надо ли задавать типы переменных цикла явно?

пусть числа = Числа[9, 8, 7]
цикл []число среди числа {
    вывод.ф("%v:%v, ", , число)
}
// вывод: 0:9, 1:8, 2:7,

Каждая переменная цикла является переменной с единственным присваиванием, её значение в теле цикла изменить нельзя.

Оператор прервать

Оператор прервать завершает выполнение самого внутреннего оператора цикла в рамках одной и той же функции.

Оператор-прервать: 'прервать' 
пока истина {
    а := а * 2
    если а > б { прервать }
}

Оператор вернуть

Оператор вернуть завершает выполнение тела функции, метода или входа, и, возможно, возращает значение.

Оператор-вернуть: 'вернуть' (Выражение | Разделитель)

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

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

фн Факториал(ц: Цел64): Цел64 {
    если ц <= 1 { вернуть 1 }
    вернуть ц * Факториал(ц - 1)
}

Если выражение в операторе присутствует, то оно должно начинаться на одной строке с ключевым словом вернуть, иначе компилятор выдаст ошибку:

Выражение должно начинаться на одной строке с ключевым словом вернуть

фн цифра?(сим: Символ): Лог {
    вернуть  // ошибка компиляции
        '0' <= сим & сим <= '9'
}

А так верно:

фн цифра?(сим: Символ): Лог {
    вернуть '0' <= сим 
      & сим <= '9'
}

Оператор авария и аварийная ситуация

Оператор авария запускает аварийную ситуация, которая, как правило, приводит к аварийному завершению программы.

Оператор-авария: 'авария' '(' Выражение ')'

Тип выражения должен быть Строка. Значение этого выражения используется в сообщении об аварийной ситуации.

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

  • Выход индекса за границы вектора при индексации
  • Операция подтверждения типа выполнена над объектом со значением пусто
  • Недопустимое преобразование типа
  • Недопустимая последовательность байт в кодировке UTF-8
  • Невозможность выделения памяти для динамического объекта
  • Нереализованная возможность в системе поддержки выполнения

Язык Тривиль не содержит средств перехвата аварийной ситуации или восстановления после него. Такие средства могут быть добавлены на уровне библиотек.