Руководство по Ada (Ada 2022)
4.2.1 Определяемые пользователем литералы
Используя один или несколько аспектов, определённых ниже, можно указать тип, позволяющий использовать один или несколько видов литералов в качестве значений этого типа.
Статическая семантика
Для типа T могут быть указаны следующие операционные аспекты, связанные с типом (в совокупности известные как аспекты определяемых пользователем литералов):
Integer_Literal
Этот аспект задаётся функциональнымименем, которое статически обозначает функцию с типом результата T и одним входным параметром типа String, который не является явно ссылающимся.
Real_Literal
Этот аспект задаётся функциональнымименем, которое статически обозначает функцию с типом результата T и одним входным параметром типа String, который не является явно ссылающимся, и необязательно второй функцией (перегружающей первую) с типом результата T и двумя входными параметрами типа String, которые не являются явно ссылающимися.
String_Literal
Этот аспект задаётся функциональнымименем, которое статически обозначает функцию с типом результата T и одним входным параметром типа Wide_Wide_String, который не является явно ссылающимся.
Аспекты определяемых пользователем литералов являются непереопределяемыми (см. 13.1.1).
Когда числовой литерал интерпретируется как значение нечислового типа T или строковый литерал интерпретируется как значение типа T, который не является строковым типом (см. 4.2), это эквивалентно вызову подпрограммы, обозначенной соответствующим аспектом T: аспект Integer_Literal для целочисленного литерала, аспект Real_Literal для вещественного литерала и аспект String_Literal для строкового литерала. Фактический параметр этого условного вызова — строковый литерал, представляющий последовательность символов, которая такая же, как последовательность символов в исходном числовом литерале или последовательность, представленная исходным строковым литералом.
Такой литерал называется определяемым пользователем литералом.
Когда именованное число, обозначающее значение типа universal_integer, интерпретируется как значение нечислового типа T, это эквивалентно вызову функции, обозначенной аспектом Integer_Literal типа T. Фактический параметр этого условного вызова — строка, содержащая текстовое представление десятичного целочисленного литерала, необязательно с предшествующим знаком минус, представляющая то же значение, что и именованное число.
Когда именованное число, обозначающее значение типа universal_real, интерпретируется как значение нечислового типа T, это эквивалентно вызову функции с двумя параметрами, обозначенной аспектом Real_Literal типа T, если таковой имеется. Фактические параметры этого условного вызова — каждая строка с текстовым представлением десятичного целочисленного литерала, первая необязательно с предшествующим знаком минус, где первая строка представляет то же значение, что и числитель, а вторая — то же значение, что и знаменатель, именованного числа, когда оно представлено как рациональное число в наименьших членах, со знаменателем больше нуля.
Правила допустимости
Аспект Integer_Literal или Real_Literal не должен быть указан для типа T, если полное представление T — это числовой тип. Аспект String_Literal не должен быть указан для типа T, если полное представление T — это строковый тип.
Для неабстрактного типа функция, непосредственно указанная для аспекта определяемого пользователем литерала, не должна быть абстрактной.
Для помеченного типа с частичным представлением аспект определяемого пользователем литерала не должен быть непосредственно указан для полного типа.
Если неабстрактный помеченный тип наследует любой аспект определяемого пользователем литерала, то каждый наследуемый аспект должен быть непосредственно указан как неабстрактная функция для типа, если только наследуемый аспект не обозначает неабстрактную функцию или функции, и тип не является нулевым расширением.
Если именованное число, обозначающее значение типа universal_integer, интерпретируется как значение нечислового типа T, то тип T должен иметь аспект Integer_Literal. Если именованное число, обозначающее значение типа universal_real, интерпретируется как значение нечислового типа T, то тип T должен иметь аспект Real_Literal, и аспект должен обозначать функцию с двумя входными параметрами, оба типа String, с результатом типа T.
Помимо мест, где обычно применяются правила допустимости (см. 12.3), эти правила также применяются в частной части экземпляра генерической единицы.
Ограниченные (временно́й) ошибки
Ошибка является ограниченной, если вычисление литерала или именованного числа, имеющего ожидаемый тип с указанным аспектом определяемого пользователем литерала, распространяет исключение. Либо Program_Error, либо исключение, распространяемое вычислением, поднимается в момент использования значения литерала или именованного числа. Если до времени выполнения распознаётся, что вычисление такого литерала или именованного числа неизбежно (если выполняется) приведёт к такой ограниченной ошибке, то это может быть сообщено как ошибка до времени выполнения.
Примеры
Примеры спецификации и использования определяемых пользователем литералов:
подтип Roman_Character является Wide_Wide_Character
с Static_Predicate =>
Roman_Character в 'I' | 'V' | 'X' | 'L' | 'C' | 'D' | 'M';
с Static_Predicate =>
Roman_Character в 'I' | 'V' | 'X' | 'L' | 'C' | 'D' | 'M';
Max_Roman_Number : константа := 3_999; -- MMMCMXCIX
тип Roman_Number является диапазоном 1 .. Max_Roman_Number
с String_Literal => To_Roman_Number;
с String_Literal => To_Roman_Number;
функция To_Roman_Number (S : Wide_Wide_String) возвращает Roman_Number
с Пред => S'Длина > 0 и затем
(для всех Char из S => Char в Roman_Character);
с Пред => S'Длина > 0 и затем
(для всех Char из S => Char в Roman_Character);
функция To_Roman_Number (S : Wide_Wide_String) возвращает Roman_Number является
(объявить
R : константный массив (Целое диапазон <>) из Roman_Number :=
(для D в S'Диапазон => Roman_Digit'Enum_Rep
(Roman_Digit'Wide_Wide_Value (''' & S(D) & ''')));
-- См. 3.5.2 и 13.4
начать
[для I в R'Диапазон =>
(если I < R'Последний и затем R(I) < R(I + 1) тогда -1 иначе 1) * R(I)]
'Reduce("+", 0)
);
(объявить
R : константный массив (Целое диапазон <>) из Roman_Number :=
(для D в S'Диапазон => Roman_Digit'Enum_Rep
(Roman_Digit'Wide_Wide_Value (''' & S(D) & ''')));
-- См. 3.5.2 и 13.4
начать
[для I в R'Диапазон =>
(если I < R'Последний и затем R(I) < R(I + 1) тогда -1 иначе 1) * R(I)]
'Reduce("+", 0)
);
X : Roman_Number := "III" * "IV" * "XII"; -- 144 (т.е., CXLIV)