Выражения

Выражение определяет вычисление значения путем применения операций к операндам.

Выражение
    : Унарное-выражение
    | Проверка-типа
    | Выражение Бинарная-операция Выражение

Унарное-выражение
    : Первичное-выражение
    | Унарная-операция Унарное-выражение
    
Проверка-типа: Выражение 'типа' Указ-типа 

Первичные выражения

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

Первичное-выражение
    : Операнд
    ( Доступ
    | Индексация
    | Преобразование
    | Вызов
    | Конструктор-вектора
    | Конструктор-класса
    | Подтверждение-типа
    )*
1
к.поле
а[]
Факториал(5)
А[1, 2, 3]
К{имя: "Вася", возраст: 23}
а^
объект.вектор[номер].метод()

Операнды

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

Операнд
    : Литерал 
    | Идентификатор 
    | '(' Выражение ')'
    | '[' ']' Указ-типа Конструктор-вектора

Тип операнда:

Операнд Тип выражения
Идентификатор тип заданного объекта
Целочисленный-литерал Цел64
Вещественный-литерал Вещ64
Строковый-литерал Строка
Символьный-литерал Символ
( Выражение ) тип выражения
[ ] Указ-типа Конструктор-вектора тип вектора: [ ] Указ-типа

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

Доступ к полям, методам и импортируемым объектам

Доступ применяется к первичному выражению.

Доступ: '.' Идентификатор

В выражении что-то.имя что-то может быть:

  • Выражением типа T, где T - это тип класса. В этом случае, имя должно быть идентификатором поля или метода типа T или базового класса T.
  • Или идентификатором импортированного модуля. В этом случае, имя должно быть идентификатором экспортируемого объекта этого модуля.

Типом выражения доступа является тип объекта, поля или метода.

чел.возраст := 5

если чел.возраст < 18 { ... }

чел.указать возраст(25) // вызов метода

Индексация

Индексация применяется к первичному выражению.

Индексация: '[' Выражение ']'
Выражение что-то[индекс] обозначает элемент вектора или вариативного параметра, индекс которого определяется выражением \verb индекс .
  • что-то должно быть идентификатором, обозначающим вариативный параметр
  • или что-то должно быть выражением типа T, где T - это тип вектора
  • индекс должен быть выражением типа Цел64 или Байт

Если во время исполнения индекс выходит за границы вектора или параметра (индекс < 0 | индекс >= длина), происходит авария.

Типом выражения индексации является тип элемента вектора, если индексируется вектор или тип вариативного параметра, если индексируется вариативный параметр.

Так как индексация и конструктор вектора не всегда различимы синтаксически, выбор между ними происходит на уровне семантики, если выражение что-то обозначает тип, то это конструктор вектора, иначе индексация.

Преобразование типа

Преобразование типа применяется к первичному выражению, и преобразует, если это возможно, значение выражения к целевому типу, указанному в операции преобразования.

Преобразование: '(:' 'осторожно'? Указ-типа ')'

Если в преобразование есть ключевое слово осторожно, то это небезопасная операция, которая рассматривается отдельно.

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

Преобразование может привести к аварийному завершению, если условие преобразования не выполнено, см. столбец Условие выполнения в таблице. Если в столбце указано нет условия, то преобразование не может привести к аварийному завершению.

Разрешенные преобразования для базовых типов и векторов байтов и символов:

Целевой тип Тип выражения Условие выполнения
Байт Цел64, Слово64, Символ, Строковый литерал длины 1 значение в диапазоне 0..255
Цел64 Байт, Символ, Строковый литерал длины 1 нет условия
Цел64 Слово64 значение в диапазоне 0..Max(Цел64)
Цел64 Вещ64 нет условия
Слово64 Байт, Символ, Строковый литерал длины 1 нет условия
Слово64 Цел64 не отрицательное значение
Вещ64 Цел64 нет условия
Символ Байт, Строковый литерал длины 1 нет условия
Символ Цел64, Слово64 значение в диапазоне, разрешенном для Unicode Символа
Строка Символ, []Символ, []Байт нет условия
[]Байт Строка, Символ нет условия
[]Символ Строка всегда

Пример преобразований:

тип Байты = []Байт
пусть байты = "Привет"(:Байты)

пусть три = pi(:Цел64) // вещественное к целому

Разрешенные преобразования для остальных типов:

Целевой тип Тип выражения Условие выполнения
Класс Ц Класс К К является базовым классом класса Ц
Класс Ц мб К, где К – класс значение не равно пусто и К является базовым классом класса Ц
Класс Ц Протокол динамический тип объекта, сохраненного в протоколе, равен Ц или является расширением Ц
Протокол П Класс динамический тип операнда реализует П
Протокол П Протокол Р динамический тип объекта, сохраненного в протоколе, реализует П
произвольный тип T полиморфный тип (*) полиморфное значение содержит значение типа T

Разделить статическую и динамическую семантику.

Преобразование полиморфного типа к базовому:

фн хочу целое(п: *) {
    пусть к = п(:Цел64)
}

Особенности преобразований:

  • Преобразование к типу класса изменяет только тип (статический тип объекта), но не представление значения.
  • Преобразование к типу протокола строит новое значение протокола.

Вызов функции или метода

Вызов применяется к первичному выражению. Это выражение

  • является идентификатором стандартной функции
  • или обозначает функцию, тогда это вызов функции
  • или явлется выражением типа класс, тогда это вызов метода
Вызов: '(' Список-аргументов? ')'
Список-аргументов: Аргумент (',' Аргумент)* ','?
Аргумент: Выражение '...'?

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

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

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

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

фн Факториал(ц: Цел64): Цел64 { /*тело*/ }

пусть рез = Факториал(5)

Развернутое выражение

Развернутое выражение может быть использовано только как аргумент для вариативного параметра и только, если это единственный аргумент для этого параметра. Если развернутое выражение х... используется как аргумент для параметра п: ...Т, то должно выполняется одно из условий:

  • х является вариативным параметром того же типа Т
  • х – это выражение типа вектора, причем тип вектора определен, как []Т

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

фн Соединить(список: ...Строка) {/*тело*/}

тип Строки = []Строка

фн Сохранить(строки: Строки) {
   пусть текст = Соединить(строки...)
   /* сохранение текста */
}

Конструктор вектора

Конструктор вектора применяется к первичному выражению, которое должно обозначать тип вектора. Результатом выполнения конструктора является вектор.

Конструктор-вектора: '[' (Значения | Элементы)? ']' 
Значения: Значение (',' Значение)* ','?
Значение: Выражение
Элементы:  Элемент (',' Элемент)* ','?
Элемент: 
    ('длина' | 'выделить' | '*' | Индекс) ':' Выражение
Индекс: Выражение

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

тип Числа = []Цел64

пусть ч = Число[] // вектор длины 0
пусть ч1 = Число[1, 2, 3] // вектор длины 3

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

тип Строки = []Строка
фн Сохранить(строки: Строки) { /*тело*/ }

Сохранить(Строки["привет", "мир"])

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

Для Элемента Х:выражение:

X выражение
длина задает длину конструируемого вектора
выделить задает число элементов, выделяемых для конструируемого вектора
* задает значение по умолчанию для всех элементов, для которых оно не задано явно
Индекс задает значение элемента с индексом Индекс

Следующие условия должны выполняться для вектора, заданного \emph{Элементами}:

  • выражение для длина должно быть \emph{совместимым по присваиванию} c типом Цел64
  • выражение для выделить должно быть совместимым по присваиванию c типом Цел64. Оно определяет число элементов, выделенных для вектора, но не обязательно использованных. Если значение для выделить меньше или равно длине, то оно игнорируется.
  • выражение для * должно быть совместимым по присваиванию с типом элемента вектора
  • Индекс должен быть выражением \emph{совместимым по присваиванию} c типом Цел64
  • Индекс должен быть константным выражение
  • выражение для \emph{Индекса} должно быть совместимым по присваиванию с типом элемента вектора
  • если длина вектора явно задана, то значение всех индексов должно быть в диапазоне [0..длина-1]. Это ограничение проверяется во время компиляции, если длина задана константным выражением, иначе во время исполнения
  • если в конструкторе задаются значения не для всех индексов, задание значения по умолчанию является обязательным
  • повторное задание значения для элемента с одним индексом является ошибкой

Если длина конструируемого вектора не задана явно, то она равна максимальному индексу + 1.

Примеры конструкторов:

тип Числа = []Цел64

пусть ч1 = Число[длина: 3, *: 0] 
// вектор [0, 0, 0]

пусть ч2 = Число[длина: 5, *:0, 1: 1, 3: 3] 
// вектор [0, 1, 0, 3, 0]

пусть ч3 = Число[*: 0, 1: 1, 3: 3]  
// [0, 1, 0, 3], длина - макс индекс + 1

Значение по умолчанию не задано:

пусть ч3 = Число[1: 1, 3: 3]  

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

пусть ч1 = Число[выделить: 100] 
// вектор [], выделено место до 100 элементов

Методы выделено, выделить

Конструктор экземпляра класса

Конструктор класса применяется к первичному выражению, которое должно обозначать тип класса. Результатом выполнения конструктора является экземпляр класса.

Конструктор-класса: '{' Список-значений-полей? '}' 
Список-значений-полей: Значение-поля (',' Значение-поля)* ','?
Значение-поля: Идентификатор ':' Значение

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

Указание нескольких значений для одного идентификатора поля является ошибкой.

Конструктор должен задавать значения для каждого поля класса с поздней инициализацией.

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

тип Человек = класс {
    имя: Строка = позже
    возраст := 0
}

пусть Вася = Человек{имя: "Вася", возраст: 25}

Значение поля “имя” с поздней инициализацией не задано:

пусть Некто := Человек{возраст: 25}

Подтверждение типа

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

Подтверждение-типа: '^'

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

пусть кличка: мб Строка = пусто
...
кличка = "Мурка"
...
напечатать(кличка^)

Унарные операции

Унарная-операция: '-' | '~' | ':~' 
Операция Действие Разрешенные типы
- отрицание Байт, Цел64, Слово64, Вещ64
~ логическое НЕ Лог
:~ инвертирование битов Байт, Цел64, Слово64

Унарные операторы имеют приоритет выше, чем бинарные, то есть выражение -х + 1 выполняется как (-х) + 1. Тип результата унарных операций равен типу операнда.

Проверка типа

Операция проверка типа проверяет динамический тип выражения.

Проверка-типа: Выражение 'типа' Указ-типа 

Операция применима к выражению типа класс, \emph{мб} класс или протокол. Тип, указанный в операции справа, должен быть типом класса или протокола. Тип результата - логический.

Для операнда мб типа со значением пусто операция возвращает ложь. Для непустых операндов:

Целевой тип Тип операнда Проверка выдает истину, если
Класс Ц Класс динамический тип операнда равен Ц или является расширением Ц
Класс Ц Протокол динамический тип объекта, сохраненного в протоколе, равен Ц или является расширением Ц
Протокол П Класс динамический тип операнда реализует П
Протокол П Протокол Р динамический тип объекта, сохраненного в протоколе, реализует П

Проверка динамического типа объекта класса:

тип К1 = класс {}
тип К2 = класс (К1) {}

пусть к1: К1 = К1{}
пусть к2: К1 = К2{}
пусть к3: мб К1 = пусто

пусть б :=  к1 типа К2 // ложь
б := к2 типа К2 // истина
б := к3 типа К2 // ложь

Бинарные операции

Бинарная-операция
    : Арифметические
    | Сравнения
    | Логические
    | Битовые
    | Сдвиги
Арифметические: '+' | '-' | '*' | '/' | '%'
Сравнения: '=' | '#' | '<' | '<=' | '>' | '>='
Логические: '|' | '&' 
Битовые: ':|' | ':&' | ':\'
Сдвиги:   '<<' | '>>'

Приоритеты операции

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

Приоритет Операция
5 * / %
4 + -
3 = # < <= > >=
2 &
1 |

Бинарные операции с одинаковым приоритетом ассоциируются слева направо. Например, x / y * z эквивалентно (x / y) * z.

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

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

Операция Действие Разрешенные типы
+ сумма Байт, Цел64, Слово64, Вещ64
- разница Байт, Цел64, Слово64, Вещ64
* произведение Байт, Цел64, Слово64, Вещ64
/ деление Байт, Цел64, Слово64, Вещ64
% остаток от деления Байт, Цел64, Слово64

Операции сравнения

Операции сравнения выдают результат типа Лог.

Все операции сравнения =, #, <, <=, >, >= применимы к операндам типов Байт, Цел64, Слово64, Цел64 и Символ. Типы левого и правого операнда, при этом, должны совпадать.

Операции =, # также применимы в следующих случаях:

Тип левого операнда Правый операнд
Лог операнд типа Лог
Строка операнд типа Строка
класс T операнд типа T
мб T пусто или операнд типа мб Т

Логические операции

Операции логическое ИЛИ (|) и логическое И (&) применимы к операндам типа Лог и выдают результат типа Лог.

  • а | б означает если а, то истина, иначе б.
  • а & б означает если а, то б, иначе ложь.

Битовые операции

Операции битовое ИЛИ (:|), битовое И (:&) и XOR – битовое исключающее ИЛИ (:\) применимы к операндам целых типов (Байт, Цел64, Слово64), оба операнда должны быть одного типа. Тип результата равен типу операндов.

Операции сдвига

Операции сдвиг влево (<<) и сдвиг вправо (>>) применимы к операндам целых типов и выдают результат того же типа, что и тип первого операнда.

Константные выражения

Константные выражения – это выражения, которые могут быть вычислены во время компиляции.

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