$$START!

VI. Дополнительно

VI. Additionally

VI. 1. Локализация строковых ресурсов 

VI. 1. Localization of string resources

 

AL-IV поддерживает механизм локализации строк с помощью псевдо-функций вида _идентификатор. Для строки это выглядит как вызов функции, например,
"Red square"._Rsq или _Rsq("Red square")

Но вызовом функции данная конструкция не является. Она лишь сообщает компилятору о том, что строку "Red square" следует поместить в массив локализуемых строк, индекс строки в этом массиве запомнить и использовать для извлечения строки из этого массива. Фактически, при этом может быть извлечена другая строка, записанная в этот массив вместо исходной строки "Red square" в результате работы функций локализации. Например, это может быть строка "Красный квадрат".

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

В качестве стандартного API для управления локализацией предлагается класс {Localize_str} (хотя это не обязательно, и всегда может быть изготовлен другой класс, работающий по другим алгоритмам).

 

The AL-IV programming language supports localization of strings via preudo-functions in form of
_identifier("Red square")
or
"Red square"._identifier

This construction looks like a function call but it is not a call to a function. But it tells to a compiler that a sting "Red square" should be placed into an array of localized strings, remember its index in the array and use it to extract the string from the localization array. Actually, another string can be extracted, if it was replacing the original one in result of working of localization operations.

All the names of such string resources (pseudo-functions) should be unique in a class.

A special class {Localize_str} is suggested to control localization process (though this is not obligatory and it is always possible to write another class to do this).

 

При использовании класса {Localize_str} следует как минимум один раз (при инициализации программы) вызвать его статическую функцию Localize, указав краткое имя языка локализации. Предполагается, что имя языка приложение сохраняет само, в доступном ему месте. Для упрощения настройки приложения в плане выбора одного из доступных для локализации языков, имеется метод List_languages, возвращающий список полных и кратких имён языков, извлечённых их названий языковых файлов (предполагая, что они именуются в едином стиле English_EN.lng).

Метод Localize имеет дополнительный строковый параметр Prefix, который позволяет переводить в программе только языковые ресурсы с именами, начинающимися с этого префикса (префикс задаётся без лидирующего подчёркивания). Пустая строка в качестве префикса означает трансляцию всех строк, независимо от имён ресурсов.

 

When using the class {Localize_str} it is necessary at least once (on an application initialization) to call a static method Localize, specifying a localization language short name. It is supposed that the name of a current language selected an application itself is storing at some place where it is get it when necessary. To simplify a setup an application to allow selecting a language by user, a method List_languages is provided returning a list of full and short names of available languages (semicolon separated) extracted from names of language files (supposing these have names in form like English_EN.lng).

The method Localize has an additional parameter Prefix which allows translating not all the strings, but only those having names starting from a certain prefix (the Prefix is provided without leading underscore character). An empty string means translating all the strings independently of resource names. 

 

При обращении к методу Localize первым делом вызывается метод Save_untranslated, если до этого он не вызывался из программы. Пользователь-переводчик всегда может обнаружить первичный языковый файл, сохранённый этим методом, сделать из него копию, поименовав её в форме Имя-языка_ИЯ.lng и отредактировав в текстовом редакторе. Строки, подлежащие переводу, имеют форму NAME=Value. Заменять следует только значения Value, не трогая имя и знак равенства. В строке могут быть и другие знаки '=', они могут изменяться в соответствии с требованиями языка. Отредактированная версия языкового файла должна быть сохранена в формате UTF-8. 

 

When the Localize method is called it is first is calling a method Save_untraslated, if it was not called before from the application. A translator always can found the primary language file saved by this method, make a copy from it, rename it to Some-language_SL.lng and edit it. Strings which should be translated has a form NAME=Value. It is necessary to replace only values leaving names as is. An edited version of a language file should be saved in UTF-8 format.

 

Местом хранения языковых файлов по умолчанию являются либо директория, в которой запускается приложение, либо (если запись туда невозможна) - специальная директория для данных приложения. Либо, имеется возможность явно указать такую директорию (метод Set_directory_lang).

 

By default a directory where an application is located is used as a place to store language files. Or if it is impossible to write there a special application data directory is used. Or it is possible to set such directory explicitly (calling Set_directory_lang). 

 

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

 

It is important that if new strings to translate were added while developing an application, then these are spread not only to an original language file stored by Save_untranslated but to all other (already translated) language files (though not yet translated).

 

VI. 2. Локализация ключевых слов языка 

VI. 2. Localization of the language keywords

 

AL-IV поддерживает возможность трансляции ключевых слов самого языка с использованием любого другого письменного языка. При этом частично используется техника локализации строк (см. выше).

Все ключевые слова канонической (английской) версии языка размещаются в текстовом файле Default_.lng в директории с исходными текстами компилятора. Достаточно скопировать этот файл и переименовать, например, в Klingon_KL.lng, после чего заменить строки на свои, и становится возможно использовать ключевые слова на соответствующем языке. Ключевые слова в языковом файле имеют имена, начинающиеся буквой K, и в основном сосредоточены в секции [{Translation_to_canonical_keyword}].

Для использования в классе национальных ключевых слов, класс должен начинаться со спецификации языка вида ['KL'] или [Language='KL'], после которой первое же ключевое слово должно быть записано уже на указанном языке. Например:

 

The AL-IV supports translation of its keywords from any human language. For this purpose a localization technique (from above section) is used partially.

All the English keywords are stored in a text file Default_.lng in a directory with source files of the compiler. It is sufficient to copy this text file, rename it e.g. to Klingon_KL.lng and replace string values from English to some other. Language keywords have names starting from the letter "K" and these are mainly in the section [{Translation_to_canonical_keyword}].

To use national keywords in your code, a class should be started from a specification ['KL'] or [Language='KL'], following which the first keyword (CLASS) is already translated into the language specified.


['RU'] КЛАСС {Привет_мир}, НЕТЕСТИРОВАН :

ФУНКЦИЯ Main|_главная : << "Привет, мир!" >> .

КОНЕЦ
 
 

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

Для этого в качестве перевода может использоваться более одного словосочетания, при этом словосочетания разделяются запятыми. Внутри словосочетания (которое может быть одним словом) вертикальный разделитель разделяет несколько возможных окончаний, которые могут продолжить основной корень. Например,
Kthis=ЭТОТ| ОБЪЕКТ,ЭТОМУ|_ОБЪЕКТУ| ОБЪЕКТУ,ЭТОГО_ОБ|ЪЕКТА,ЭТОГО ОБЪЕКТА

- позволяет использовать вместо THIS слова и словосочетания:

  • ЭТОТ
  • ЭТОТ ОБЪЕКТ
  • ЭТОМУ
  • ЭТОМУ_ОБЪЕКТУ
  • ЭТОМУ ОБЪЕКТУ
  • ЭТОГО_ОБ
  • ЭТОГО_ОБЪЕКТА
  • ЭТОГО ОБЪЕКТА

Примечание: в языке AL-IV ключевое слово THIS бывает необходимо в основном в двух случаях:

  • когда сам объект должен быть передан в качестве параметра,
  • или когда создаётся новый объект, и объект THIS указывается в качестве его владельца:
    x = {Тип}( ... ), ПРИНАДЛЕЖАЩИЙ ЭТОМУ ОБЪЕКТУ - прямой перевод
    x = {Type}( ... ), OWNED BY THIS
 

Since a translation language can have some word morph rules or have some variations depending on a lexical context, especially for keywords it is allowed to relate to a single canonical keyword more then a single identifier.

A translation can have a form of several word sequences comma separated, where each sequence have a form root|suffix1|fuffix2|..., and any suffix can have a space or start from a space. In result, all the words root, rootsuffix1, rootsuffix2, ... are corresponding to the keyword specified (and a space in a suffix means that two identifiers spaces/tabs separated are treated as the target keyword, too).

 

Вместе с ключевыми словами локализуются кодированные символы (#NL, #TAB и т.п.), а так же встроенные псевдо-функции (.Len, .Str, .Index и др.) Но в языке имеется так же возможность упростить перевод любых ранее изготовленных классов на национальные языки. Для этого для транслируемого класса создаётся класс-зеркало на соответствующем языке, с единственным модификатором в заголовке TRANSLATION OF {Class_name}. Он должен содержать только переводы наименований для полей, функций (и их параметров), перечислений, записей, констант. Переименования помещаются в соответствующие секции. Например:

 

Together with the language keywords, encoded characters are translated (#NL, #TAB etc.) and some embedded pseudo-functions such as (.Index, .Len, .Str). And it is allolwed to translate whole classes to national languages. To do so, for a translating class a special mirror class is created which have a single modifier TRANSLATION OF {Class_name}. It contains only translations for fields, functions, methods, enumerations, records, constants, placed in correspondent sections.

['RU']
КЛАСС {Текст_файл}, ПЕРЕВОД {Text_file} :

ПОЛЯ:
   Path = Путь|_к_текстовому_файлу
   Encoding = Кодировка
.

МЕТОДЫ:
   Load = Загруз|ить(Строки|_массив[])
   Save = Сохран|ить(Строки|_массив[])
.

ФУНКЦИИ:
   Text_load = Текст_загруз|ить(
      Путь|_к_текстовому_файлу, Строки|_массив[] )
   Text_save = Текст_сохран|ить(
      Путь|_к_текстовому_файлу, Строки|_массив[] )
.

КОНЕЦ
 
 

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

 

Then it is sufficient to include such translation class into import and call its methods, functions, fields etc. using translated names.

 

Примечание для русскоязычных читателей. Возможность по русификации (или точнее, произвольной локализации) предоставляется как механизм, позволяющий развивать навыки программирования в любом возрасте, без необходимости начинать с изучения английского языка. Несмотря на довольно большой список слов в словаре, основа языка АЛФОР требует значительно меньше важных ключевых слов для того, чтобы можно было начать писать свой код или понимать уже написанный. Например, класс для вычисления вещественных корней квадратного уравнения:

 

['RU']
КЛАСС {Квадратное_уравнение}, НЕТЕСТИРОВАН:
ИМПОРТ: {Математика} .
ВЕЩЕСТВЕННОЕ A|_коэффициент_не_должен_быть_0
ВЕЩЕСТВЕННОЕ B|_коэффициент
ВЕЩЕСТВЕННОЕ C|_коэффициент

МЕТОД Решение\_квадратного_уравнения( Ответ\ы_в_массиве[] )
      :
     
Ответ[].Стереть
      --------- 'A == 0 -
неверное условие'
      ЕСЛИ Абсолютное_значение(A) < 0.000_000_001 ? ==> ;
      ВЕЩЕСТВЕННЫЙ д\искриминант = B * B - 4 * A * C
      ЕСЛИ ?
      [д == 0] ? Ответ[] << -B / (2 * A)
      [
д > 0 ] ? ВЕЩЕСТВЕННЫЙ к\орень_из_д = д.Корень
               
 Ответ[] << (-B - к) / (2 * A)
               
 Ответ[] << (-B + к) / (2 * A) ; .

КОНЕЦ
 
 

Формат класса для перевода на другой язык, в форме диаграммы:

 

A class format for a translation class, in form of a diagram:


 

VII. 3. STORE - скрытые параметры 

VI. 3. STORE - hidden parameters

 

Модификатор STORE для метода создаёт скрытый (для вызывающей стороны) целочисленный параметр, значение которого сохраняется на стороне вызывающего объекта.

В модификаторе задаётся имя параметра, как его видит функция, и может быть задано первоначальное значение:

, STORE(Имя = значение)

 

A modifier STORE for a method creates hidden (for a caller side) integer parameter which value is stored on a caller side.

A name of a parameter is specified in parenthesis, and also its initial value can be specified additionally:

, STORE(Name = value)

 

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

 

For each method call in a calling class a hidden integer field is created for the stored parameter, and this field is passed to the method by a reference implicitly. Passing by a reference means that if the method changes a STORE-parameter, then after it is finished, new value become the value of the hidden field. And on the next call of the same method in the same line of code, this new value will be passed as an additional parameter.

 

Для того, чтобы "забыть" сохранённые значения параметров, может использоваться вызов метода с модификатором FORGET. При этом будут сброшены в начальное состояние (указанное в модификаторе STORE) значения всех полей, которые создавались для передачи в скрытые параметры методов того же класса,  что и метод с модификатором FORGET.

 

 

To reset stored hidden parameter values it is possible to call a method having a modifier FORGET. Stored parameters created to pass them to methods of the same class as the FORGET method will be set to its initial values (specified in the STORED modifier).

 

Сочетание модификаторов STORE и FORGET позволяет ускорить доступ к массивам данных, индексируемым константными строками. Например, при получении значений полей записей в выборке SELECT из базы данных, или при доступе к колонкам элемента в listview по их заголовкам.

Пример реализации доступа к колонкам listview по именам:

 

 

Modifiers STORE and FORGET together allow speed up accessing data arrays indexing by constant strings. E.g., while obtaining values of fields of records in SELECT results from database, or while accessing items and sub-items of a list view by headers of columns.

An example of accessing listview by column names:

FUN Cells|_by_constant_names(
    INT Row|_index,
    STR Name|_of_cell) ==> STR, STORE(I|ndex_of_name = -1),
    
                            RESTRICT Name IS CONST
    :
CASE I < 0 ? I = Column_index(Name) ;
RESULT = Subitems(Row, I) .
 

В данном примере, модификатор FORGET должны иметь все методы, меняющие состав колонок listview и их названия.

 

In the above example, the FORGET modifier should be assigned to all the methods changing list view columns set and its names.

 

VI. 4. SQL запросы 

VI. 4. SQL queries

 

Если в классе определён метод Write(STR), то для объектов такого класса разрешена операция объект << строка, фактически вызывающая этот метод.

В случае класса {DB}, предоставляющего интерфейс для работы с базами данных, операция << может (и должна) использоваться для установки текста SQL-запроса (в поле SQL, которое доступно только по чтению).

 

 

If a class has a method Write(STR), then for its objects it is possible to use operation

OBJECT << STRING

 

It is actually just calls the Write method.

In case of class {DB} (which is intended to work with databases), the operation << should be used to set up an SQL query text.

 

При установке текста SQL, если текст строкового выражения начинается с одного из ключевых слов INSERT / DELETE / UPDATE / SELECT, то вся строка представляет из себя SQL-подобный оператор, который формирует текстовую строку с SQL-запросом. При этом если не все, то многие параметры SQL-утверждения могут быть проконтролированы на этапе компиляции кода, что существенно снижает вероятность возникновения ошибок на этапе выполнения программы.

 

When a text of SQL is set and string expression is started from one of keywords INSERT / DELETE / UPDATE / SELECT, then entire expression is treated as an SQL-like statement which creates a final SQL text at run time. And if not all but many of parameters of such SQL-like statement are checked at compile time, minimizing possible problems at run time later.

 

Такие SQL-запросы ссылаются на таблицы, декларируемые утверждениями TABLE (ссылающимися на декларации записей RECORD). Например:
 

 

Such SQL queries a referencing "tables" declared by TABLE declarations (on base of RECORDs). E.g.:

RECORD {abiturient}:
        REAL ID|entity_counter_64bit
        STR Surname|_last_name, MAXLEN[40]
        STR FirstName, MAXLEN[40]
        STR MiddleName, MAXLEN[40]
        INT Exam1|_scores
        INT Exam2|_scores
        INT Exam3|_scores
        {date_time} D|ate_in_documents
        BOOL OriginalDocuments
        BOOL AgreeToEnroll .


TABLE Students|_want_to_be:
        {abiturient}
        NAME "Students"
        COUNTER(ID)
        NOTNULL(Surname, FirstName, D)
        NAMES(D = "DateJoin") .
 

Запросы SQL-подобны, но их синтаксис несколько изменён по сравнению со стандартом SQL:

  • Все ключевые слова записываются в верхнем регистре: SELECT, UPDATE, INSERT, DELETE, DISTINCT, TOP, FROM, AS, INTO, WHERE, GROUP, ORDER, BY, IN, NOT, IS, NULL, JOIN, ON, LEFT, OUTER;
  • все части SQL-подобного утверждения разделяются запятыми (что упрощает перенос на другую строку, в соответствии с правилами AL-IV). Например:
    SELECT DISTINCT, FROM Students, (*)
  • часть FROM-JOINS следует в операторе SELECT до списка выбираемых полей (в начале оператора, как в DELETE и UPDATE);
  • Список выбираемых значений, а так же полей для сортировки и группировки, всегда заключается в круглые скобки;
  • Идентификаторы по возможности считаются ссылками на таблицы, алиасы таблиц, имена полей, и только при несовпадении со всеми ожидаемыми идентификаторами из этих списков, далее рассматриваются компилятором как части AL-IV-выражения (имена переменных, функций и т.п.). Например:
    UPDATE Students, SET Exam3 = 64,
    WHERE ID = {Ident_to_update}
  • При необходимости явно указать, что часть кода относится не к синтаксису SQL, а к синтаксису AL-IV, выражение AL-IV заключается в фигурные скобки. В части WHERE это практически единственный способ использования значений, вычисленных в коде AL-IV;
  • Все такие выражения должны иметь один из типов данных INT / REAL / BOOL / STR / {date_time}. Другие типы не допускаются (как исключение: тип BYTE может рассматриваться как целочисленный);
  • Для представления счётчиков (автоинкрементных полей) следует использовать тип REAL, т.к. разрядности обычного целого недостаточно для представления длинного целого, использующегося в счетчиках;
  • Выражения AL-IV автоматически преобразуются в строку, воспринимаемую БД. Нет необходимости (и запрещено) самому преобразовывать параметрические значения в строки с использованием функция Bool_sql, Int_sql, Str_sql, Date_sql, Real_Sql;
  • В операторе INSERT, в отличие от T-SQL и подобно UPDATE, список полей и присваиваемых значений задаётся парами
    ИМЯПОЛЯ = ЗНАЧЕНИЕ. Например:
    db << INSERT INTO Students,
          Surname = "Origatsu",
          FirstName="Pei",
          MiddleName="Q",
          Exam1=84,
          Exam2=81,
          Exam3=92,
          D=Date(2017, 7, 14),
          OriginalDocuments=TRUE

    db.Exec
  • В операторах INSERT и UPDATE, возможно указать переменную (или выражение) типа записи, соответствующей таблице, для занесения содержимого этой записи в таблицу, вместо перечисления большого числа полей:
    db << INSERT INTO Students, BY Rec
    db.Exec
  • Если запрос использует более одной таблицы, то единственный способ подключить дополнительные таблицы - это утверждения JOIN, следующие за FROM (или за первой таблицей - в UPDATE). При этом все таблицы, включая основную, должны иметь уникальные алиасы, и далее обращение происходит к полям в виде алиас . поле, например:
    db << SELECT FROM Students a,
          JOIN Students b ON a.Exam1 = b.Exam1,
          (  a.ID         AS a_ID,
             a.FirstName  AS a_F,
             a.MiddleName AS a_M,
             a.Surname    AS a_S,
             COUNT(b.*)   AS CNT_b ),
          GROUP BY (a_ID)
    db.Open

     
 

Queries are similar to SQL but syntax is different a bit:

  • All the keywords are written in uppercase: SELECT, UPDATE, INSERT, DELETE, DISTINCT, TOP, FROM, AS, INTO, WHERE, GROUP, ORDER, BY, IN, NOT, IS, NULL, JOIN, ON, LEFT, OUTER;
  • All the parts of an SQL-like statement are separated with comma to simplify splitting long queries onto lines. E.g:
    SELECT DISTINCT, FROM Students, (*)
  • A FROM-JOINs part is located always in the beginning of the statement, even for SELECT;
  • A list of selected fields and values as well as lists of grouping and ordered values is enclosed into parentheses;
  • If it is possible, all identifiers first are treated as references to tables, table aliases and field names. And only in cases when these are not matching correspondent identifiers, these are attempted to use as AL-IV variables, functions etc. E.g.:
    UPDATE Students, SET Exam3 = 64,
    WHERE ID = {Ident_to_update}
  • If it is necessary to specify explicitly that part of text in a statement is an AL-IV text, the AL-IV expression is enclosed into figure brackets. In a WHERE clause this is the only way in many cases to use values calculated at run time;
  • All AL-IV expressions used directly in an SQL expression should have types INT / REAL / BOOL / STR / {date_time}. Other types are not allowed (but BYTE an many cases is treated as INT);
  • To represent counter fields (auto-incrementing) it is necessary to use type REAL, still ususal integer type precision is not enough to represent long integer value, which are used for counters in databases;
  • AL-IV expressions are automatically converted to SQL compatible strings, to treat those in SQL queries as indirect constants. So it is not necessary (and not allowed) to convert these into strings using such functions as Bool_sql, Int_sql, Str_sql, Real_sql, Date_sql;
  • In the INSERT statement, a list of fields and assigned values is specified like in the UPDATE:
    db << INSERT INTO Students,
          Surname = "Origatsu",
          FirstName="Pei",
          MiddleName="Q",
          Exam1=84,
          Exam2=81,
          Exam3=92,
          D=Date(2017, 7, 14),
          OriginalDocuments=TRUE
    db.Exec
  • In INSERT and UPDATE statements it is allowed to specify a variable (or other expression) of correspondent to table RECORD to set values, instead of listing all the fields and its new values. E.g.:
    db << INSERT INTO Students, BY Rec
    db.Exec
  • If a query contains more than a single table, the only way to specify additional tables is in adding JOIN parts just after the first table. In such case, all the tables should have unique aliases specified to make it possible to reference its fields. E.g.:
    db << SELECT FROM Students a,
          JOIN Students b ON a.Exam1 = b.Exam1,
          (  a.ID         AS a_ID,
             a.FirstName  AS a_F,
             a.MiddleName AS a_M,
             a.Surname    AS a_S,
             COUNT(b.*)   AS CNT_b ),
          GROUP BY (a_ID)
    db.Open
 

Ниже приводятся синтаксические диаграммы для операторов, формирующих SQL-запросы:

 

There are syntax diagrams below for statements generating SQL queries:

 

VI. 5. Отменённые и устаревшие классы, записи, перечисления, поля, функции 

VI. 5. Abandoned and deprecated classes, records, enumerations, fields, functions

 

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

Чтобы иметь возможность управлять на уровне языка отмиранием ненужных старых возможностей, и обеспечивать плавный переход к использованию в новых версиях продукта только новых возможностей, в язык введены модификаторы
DEPRECATED('текст') и ABANDONED('текст').

  • Эти модификаторы применимы к классам, записям (RECORD), полям классов и записей, перечислениям, функциям.
  • Текст должен содержать имя альтернативы (если такая имеется) или хотя бы краткое пояснение, почему возможность отменяется или устаревает. Содержимое текста компилятором, разумеется, не проверяется.
  • Модификаторы DEPRECATED и ABANDONED взаимоисключающие.
  • Предлагается сначала для устаревающих понятий добавить модификатор DEPRECATED, и сохранять его до тех пор, пока устаревшая (но как-то работающая) старая возможность ещё поддерживается.
  • При использовании устаревающей возможности в использующем коде, компилятор будет выдавать предупреждение, содержащее указанный в модификаторе текст.
  • Если добавлен модификатор ABANDONED, соответствующий элемент уже нельзя использовать: компилятор будет сообщать об ошибке. При этом для функций можно убрать тело, для класса - тело всех функций.
  • Смысл сохранять память об удалённой декларации заключается в том, чтобы предоставить использующему класс программисту информацию об альтернативе (сообщение 'текст' может содержать имя альтернативной функции, класса, или несколько таких имён, может быть, ссылку на Web-ресурс и т.п.).
 

While your classes and libraries are developing, you can find it necessary to re-write some things. Some functions, fields and so on becomes too old, supporting it costs more and more, some things are replaced with others etc.

To make it possible to control on a language level of a process of a distinction of old entities and provide smooth transition to new features, following modifiers are added to the AL-IV:

DEPRECATED( 'text' ) and ABANDONED( 'text' ).

  • These modifiers are applicable to classes, records (RECORD), fields of classes and records, enumerations and functions.
  • A text in parentheses (and apostrophes) should contain a name of an alternative or at least a short clarification why the feature is marked as DEPRECATED or ABANDONED).
  • Modifiers ABANDONED and DEPRECATED are mutually exclusive.
  • It is suggested for all things become obsolete to add a modifier DEPRECATED, and keep it until the feature become not supported at all, and then replace it to ABANDONED.
  • When a deprecated feature is used in code, a compiler generates a warning containing the text specified in the modifier.
  • If the modifier ABANDONED is added then correspondent feature can not be used: compiler will generate an error. For an abandoned function, it is possible to remove its body, for abandoned class - to remove bodies of all the functions.
  • The sense in storing information about abandoned features in a class code is in providing an information about an alternative (in the text in parentheses which should contain name of such alternative function, class or other way to get desired functionality).
 

Второе применение модификаторов DEPRECATED и ABANDONED - это модификация функциональности в классе-наследнике путем отказа от использования части методов, полей. Или по той причине, что метод требует совершенно иного набора параметра, или его работа существенно отличается от работы аналогичного метода предка.

Например, при создании класса {Dialog} как наследника класса {Form}, метод Show декларирован как ABANDONED('Show_modal'). Это сделано для того, чтобы при использовании форм класса {Dialog} программист получал от компилятора сообщение об ошибке при попытке вызова метода Show.

К сожалению, сообщение об ошибке от компилятора не будет получено в том случае, если программист присвоил свою форму класса {Dialog} переменной класса {Form}. В этом случае будет вызван отмененный метод (и если он не содержит код, этот вызов будет проигнорирован).
 

 

 

VI. 6. Ограничения на значения параметров

VI. 6. Restrictions to parameter values

 

Функция может иметь модификаторы RESTRICT (безусловное ограничение на параметр) и IF/THEN (условные ограничения на значения одних параметров в зависимости от значений других параметров). При этом:

  • Все такие ограничения на параметры записываются после всех прочих модификаторов функции.
  • Количество ограничивающих модификаторов не ограничено.
  • Если устанавливается ограничение на параметр, или параметр участвует в части IF условного ограничения, то в качестве значения параметра можно передать только константу. Исключением является случай условия
    IF параметр IS CONSTANT, THEN ...
  • Ограничения на параметры контролируются компилятором на этапе компиляции кода. Если условия, указанные в ограничениях, не выполняются, компилятор выдаёт сообщение об ошибке.
  • Параметр может быть ограничен или участвовать в условии ограничения, если он имеет простой тип (BOOL, BYTE, INT, REAL, STR, или перечисление - ENUM), и не является массивом.
 

A function can have modifiers RESTRICT (unconditional restriction on a parameter) and IF/THEN modifiers (conditional restrictions on parameter values depending on values of other parameters).

  • All such restrictions on parameters are written following all other function modifiers.
  • Amount of restrict modifiers is not restricted.
  • If a restriction is specified for a parameter, or the parameter is used in the IF part of a conditional restriction, then it is possible to pass only a constant as a value of such parameter. The only exclusion is a special conditional restriction
    IF parameter IS CONSTANT, THEN ...
  • Restrictions on parameters are controlled by a compiler at a compile stage. If conditions specified in restrictions are not satisfied, the compiler fires an error.
  • A parameter can be restricted or participate in a condition of a restriction only if it has a simple data type (BOOL, BYTE, INT, REAL, STR) or it has a enumeration data type, and is not an array.
 

Безусловное ограничение имеет форму:
RESTRICT имя_параметра IN [список_значений]

или (для вещественных типов):
RESTRICT имя_параметра IN [N1 TO N2]

или (без указания значений):
RESTRICT имя_параметра IS CONSTANT

  • Второй вариант (с диапазоном) используется для вещественных параметров. Проверка идёт по условию
    N1 <= X <= N2,
    где:
    • X - переданное функции значение,
    • N1 и N2 - границы отрезка [N1 TO N2].
  • Третий вариант не определяет проверку значений, но обязывает передавать в функцию только константное значение.
 

Unconditional restriction has a form
RESTRICT parameter_name IN [list_of_values]

 

or (for REAL data type):
RESTRICT parameter_name IN [N1 TO N2]

 

or (without specifying certain value):
RESTRICT parameter IS CONSTANT

  • When a range is used (for real values), a condition is checking N1 <= X <= N2, where:
    • X - a value passed to a function,
    • N1 and N2 - bounds of a range [N1 TO N2].
  • The last variant does not specify any value checks but requires passing only a constant value as the parameter specified.
 

Условное ограничение имеет форму:
IF параметр1 IN [список или диапазон],
THEN
параметр2 IN [список или диапазон]

 

или
любая часть IN [...] может заменена на IS CONSTANT.

  • Оба параметра могут принимать только константы (кроме случая IF p1 IS CONSTANT)
  • При выполнении условия в части IF, обязано выполняться условие в части THEN, чтобы компилятор не вызывал ошибку.
  • Если условие в части IF не выполнено, часть THEN просто игнорируется (но параметр всё равно может принимать только константу).
 

 

A conditional restriction has a form:
IF parameter1 IN [list or range],
THEN parameter2 IN [list or range]

 

or part IN [...] can be replaced with IS CONSTANT.

  • Both parameters listed accept only constant values (except the case IF parameter1 IS CONSTANT).
  • If a condition in the IF part is satisfied, then a condition in the THEN part must be satisfied to avoid compiler errors.
  • If the IF part condition is not satisfied, then the THEN part is just ignored (but the parameter 2 anyway can accept only a constant).
 

зачем это может понадобиться?

 

Например, для реализации набора переходников к библиотеке OpenGL. Можно упростить работу, просто собрав переходники к соответствующим функциям, и передавать так же целочисленные константы, но добавить ограничивающие модификаторы, чтобы обеспечить проверку на недопустимые наборы значений параметров. Это намного проще, чем строить системы классов или использовать для "чистоты" перечисления, и обеспечивает тот же уровень защиты (если не лучший). И при этом работают классические примеры (того же NeHe), практически без изменений (достаточно убрать символы ';' в конце операторов).

 

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

 

 When this can be useful ?

 

E.g., to implement wrappers to the OpenGL library. It is possible to simplify the work just creating simple wrappers to correspondent OpenGL functions and pass just integer constants, but to add restrictions for such parameters to provide checking for only allowed sets of constants. This is much more simple then to create a set of classes or to use enumerated values (since the same values can be used in different functions for different purposes). Restrictions are providing working of classic OpenGL code (e.g. samples from NeHe etc.), almost without any changes (it is sufficient to remove symbols ';' at ends of lines).

 

This restrictions feature can be used (in future) to optimize code: if the compiler "knows" that only constants can be passed, it can create for each constant a special function version optimized for such constant value.

 

VI. 7. Контроль зацикливания

VI. 7. Infinitive loop control

  1. На каждой итерации любого цикла уменьшается внутренний глобальный счётчик. По достижении значения 0 вызывается внутренняя процедура, которая снова устанавливает значение глобального счётчика (обычно это 65535, и может вызывать дополнительные действия.
  2. Для визуальных приложений, имеющих графическую оболочку, после достаточно длительного выполнения начатой длинной операции (обычно более 2 секунд), может отображаться текущий прогресс и краткое описание начатой операции и её стадии выполнения. А так же кнопка для принудительной отмены операции.
  3. В случае, если программист не предусмотрел начало длинной операции, тем не менее нажатие на кнопку, пункт меню, и т.п., так же считается точкой начало длительной операции "по умолчанию". Для такой операции так же может отображаться "прогресс", и такая операция так же может быть отменена автоматически.
  1. On each iteration of any loop, an internal global counter is decreased. When it becomes equal 0, an internal procedure is calling which restores that counter to an initial value (usually 65536), and an additional function can be called in such case.
  2. For visual applications having graphics shell after sufficient time of long operation execution (usually more then 2 seconds) a progress of the operation can start displaying which is showing a percentage an a description of the long operation. And it is possible to cancel the operation by a button on the progress panel.
  3. In case when a programmer did not provide such long operation therefore pressing a button or a menu always is considered as a start of possible such long operation by default. So for such operation also it is possible to display a progress automatically allowing to cancel the operation if it becomes to work too long.

 

 

 

$$STOP!

$$OUT=-