Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
MUMPS СУБД. Практика применения и опыт программирования.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
2 Мб
Скачать
7.4. ПРЕПРОЦЕССОР 471
данных, так и в значениях индексов.
В практическом применении блочный экспорт используется, насколь­ко автору известно, редко и, в основном, для переноса больших объемов данных, либо при необходимости выполнять быстрый экспорт. Тради­ционно таким способом переноса данных пользуются в основном разра­ботчики и администраторы для переноса данных между собственными серверами.

7.4 Препроцессор

Препроцессор относится к нестандартным дополнениям MUMPS систем, не предусмотренным стандартом, но поддерживаемым различными про­изводителями из соображений практической полезности.
В отличие от языков семейства Си, где препроцессор является неотъ­емлемым атрибутом, изначально и по умолчанию присутствующим в рас­поряжении программиста, в языке MUMPS его нет, и поддержка выпол­няется в каждой из систем самостоятельно производителями. При этом различные реализации поддерживают большинство основных особенно­стей для синтаксической совместимости исходных текстов и различные собственные особенности препроцессинга рутин.
Основной единицей трансляции в строчно - ориентированном язы­ке является совокупность строк рутины. Именно по этой совокупности строк система исполнения отсчитывает смещения относительно меток. И задачей препроцессора является получение такого непосредственного кода на языке MUMPS. Если в языке Си номера строк до препроцессиро­вания сохраняются для отладчика и директивы __LINE__, то в системах MUMPS такое соответствие не сохраняется и нет прямого соответствия между номером строки в рутине с макросами и номером строки для системы построчного исполнения. В определенном смысле это может до­ставить непривычные неудобства при первых применениях.
Препроцессор, несмотря на то, что появился в MUMPS системах мно­го лет назад, до сих пор применяется редко и большинство разработок, особенно ориентированных на возможность портирования на различные реализации, его не используют.
По организации работы большинство препроцессоров использует три вида рутин - стандартные рутины, определенные в языке MUMPS, назы­ваемые также рутинами непосредственного кода (INTermediate routines), рутины содержащие макросы - макрорутины (MACro routines) и рутины, предназначенные для включения при препроцессировании - включаемые рутины (INClude routines).
472 ГЛАВА 7. ПРАКТИКА ПРИМЕНЕНИЯ
Имена различаются при их использовании по условному расширению,
например:
ROUNAME.MAC ROUNAME.INC ROUNAME.INT
Существуют также реализации MUMPS систем, в которых органи­зация макрокода иная, и различаются только два типа рутин - INT и MAC, при этом считается, что в качестве включаемых (INC) рутин ис­пользуются MAC рутины с соответствующим именем.
Поддерживаются правила компиляции: 1) INC рутины не компили­руются и не порождают исполняемый байткод, но используются MAC рутинами для включения, 2) INT рутины компилируются в исполняе­мый байткод и 3) MAC рутины транслируются препроцессором макро­сов в INT рутину с тем же именем, после чего она компилируется в исполняемый байткод.
Макрорутины по своему назначению являются исходным текстом для получения INT рутины и последующего исполняемого байткода. При препроцессировании препроцессор сканирует строку за строкой после­довательно и, если в строке есть директивы препроцессора, то строка соответствующим образом изменяется или не включается в выходную INT рутину. Рутина с макросами может содержать просто текст на язы­ке MUMPS без директив препроцессора, смешивание MUMPS кода с директивами препроцессора, или одни только директивы на усмотрение программиста.
Рутины разных типов могут иметь одинаковые имена. Текст рутин хранится в разных глобалах. После трансляции MAC рутины получен­ный INT код по умолчанию доступен для редактирования и просмотра, в некоторых реализациях может использоваться дополнительная опция не сохранять сгенерированный промежуточный код INT рутины.
Макросы поддерживаются только в макрорутинах. В косвенных вы­ражениях, в аргументе команды XECUTE, и в командном режиме мак­росы не поддерживаются. В этом случае код и подстановки косвенности выполняются вне контекста препроцессора.
Для совместимости с традиционной разработкой без использования макросов обычно поддерживается два вида трансляции - ориентирован­ный на чистый MUMPS код и ориентированный на препроцессор. При этом, если присутствует MAC рутина, то она сначала препроцессируется для получения INT кода, и дальше вызывается транслятор INT кода. Ес­ли было указано явно расширение (или тип) рутины, то компилируется именно этот вариант.
7.4. ПРЕПРОЦЕССОР 473
В общем случае инструкции препроцессора состоят из директив пре-
процессора и макроподстановок.
При трансляции макрокода директивы выполняются, изменяя состо­яние внутренних определений препроцессора, или управляя условием обработки кода. Если препроцессор встречает макроподстановку, то за­меняет ее по месту на ее определение с возможными указанными для нее параметрами.
Директивы практически всех препроцессоров состоят из условных директив
1. #if
2. #ifdef
3. #ifndef
4. #else
5. #endif
директив определения макроподстановок
1. #define
2. #undef
и директив управления
1. #include
2. #execute
В зависимости от реализации MUMPS системы могут поддерживать также дополнительные директивы и способы макроподстановок.
Условные директивы указывают препроцессору на необходимость или продолжить обработку или не выполнять обработку группы строк до окончания действия директивы или до переключения условия директи­вой #else. При этом директивы #ifdef и #ifndef проверяют существует ли определение макроподстановки, а директива #if вычисляет MUMPS выражение и программист может обращаться ко всем возможностям MUMPS системы, например вызвать функции или прочитать значение глобалов.
Директивы #define и #undef создают или удаляют определение мак­роподстановки.
474 ГЛАВА 7. ПРАКТИКА ПРИМЕНЕНИЯ
Директива #include включает указанную директиве INC рутину и препроцессор продолжает обработку кода с первой строки этой рутины, как если бы ее текст был целиком вставлен вместо строки с директивой #include.
Директива #execute выполняет аргумент как последовательность ко­манд языка MUMPS, как если бы они были аргументом команды xecute. В этой директиве, как и в директиве #if, разработчик может обращаться к функциям, глобальным или локальным переменным.
Макроподстановки развертываются в их определение, и в опреде­лении макроподстановки и в качестве их аргументов также могут ис­пользоваться другие макроподстановки. Генерируемый код INT рутины порождается на момент препроцессирования так, как определено макро­сом. В частности, если макрос вычисляет значение, зависимое от време­ни, или от версии, или от состояния глобалов на момент трансляции, то порожденный INT код будет содержать именно эти значения.
В большинстве случаев препроцессор макросов используется в каче­стве простого инструмента подстановки. Но при этом механизм препро­цессирования довольно мощный, и может порождать другие рутины, или генерировать текст, вызывая сложные функции. Технически разработчи­ку доступны все возможности самой MUMPS системы для генерации подстановок или выполнения произвольных действий на момент транс­ляции макрокода.
Общая схема подстановок состоит в определении имени макроса и его аргументов и в использовании его там где необходимо выполнить такую подстановку. Например, код:
#define DGLO(%id) ^AR67ED("tools",46,%id)
... s $$$DGLO(idrec)=$$value(idrec)
развертывается в код
s ^AR67ED("tools",46,idrec)=$$value(idrec)
Основной задачей препроцессора макросов, таким образом, является помощь разработчику в упрощении разработки и в сокрытии длинных и, возможно, не очень читабельных строк в осмысленные с точки зрения разработчика синтаксические конструкции. В техническом отношении сложность макросов может быть произвольной.
Рутины, предназначенные для включения в макрорутины, использу­ются преимущественно для создания набора определений макросов. Но также могут содержать и традиционный MUMPS код. В этом случае
7.4. ПРЕПРОЦЕССОР 475
он будет включаться в генерируемую INT рутину как он указан. Вклю­чаемые INC рутины могут включать другие включаемые INC рутины и макрорутина (MAC) может включать несколько включаемых INC рутин.
Кроме того, что определение макросов создает более читабельный для разработчика словарь терминов, упрощая написание программ и устраняя возможность опечаток, вторым практическим преимуществом прероцессора является согласованность MAC рутин по использованию магических констант, определенных в одном месте, во включаемой INC рутине.
К третьему практическому преимуществу относится то, что препро­цессоры в MUMPS системах используют проверку, существует ли опре­деление для подстановки, прежде чем ее выполнить. В случае если ее не существует, препроцессор выдает диагностическое сообщение об ошиб­ке трансляции. Это поведение дает разработчикам механизм проверки на опечатки в именах переменных или функций или рутин. Для самого языка MUMPS при трансляции строки не важно, существует ли такая переменная или рутина, он не имеет такой информации. Но препро­цессор уже может проверить опечатки по текущему набору определений макроподстановок. При разработке крупных прикладных систем, исполь­зующих множество имен переменных, функций и рутин, такие проверки могут существенно сократить число ошибок уже на этапе кодирования.
Хотя препроцессор и поддерживается различными системами, но к его недостаткам можно отнести то, что в MUMPS системах не встре­чается утилита, аналогичная утилите MAKE, чтобы автоматически пе­рекомпилировать MAC рутины, если изменились включаемые ими INC рутины или перекомпилировать INT рутины если изменились соответ­ствующие им MAC рутины. Тем не менее, такую утилиту можно соста­вить самостоятельно, с учетом особенностей прикладного проекта.
Рассмотрим в качестве примера построение таких зависимостей. Для того, чтобы вести информацию о зависимостях MAC файлов от INC фай­лов, необходимо где-то дополнительно сделать отметку о том, что при трансляции MAC файла были транслированы включенные INC файлы. Пусть такая зависимость ведется в глобали
^DEPENDS(macroutine,incroutine)=""
При препроцессировании INC рутины необходимо выполнить отметку о том, что она препроцессировалась:
#execute s ^mtemp1("INC",$j,incroutine)=""
Эту строку вставляем в текст INC рутины.
476 ГЛАВА 7. ПРАКТИКА ПРИМЕНЕНИЯ
При препроцессировании MAC рутины просто вносим записи, полу-
ченные при обработке INC рутин:
#execute k ^DEPENDS(macroutine) #execute m ^DEPENDS(macroutine)=^mtemp1("INC",$j) #execute k ^mtemp1("INC",$j)
Эти строки вставляем в текст MAC рутины. Удаление пройденных INC рутин нужно для того, чтобы информация о транслированных INC рутинах не использовалась при последующей трансляции другой MAC рутины.
При трансляции таких рутин будут автоматически заполняться зави­симости MAC рутин от INC рутин в глобале
^DEPENDS(macroutine,incroutine)=""
Далее остается получить имена MAC рутин и INC рутин либо ав­томатически используя особенности препроцессора, либо явно указав текущее имя, например если рутина ETRAP.INC, то вставить строку
#execute s ^mtemp1("INC",$j,"ETRAP")=""
Далее, при реализации утилиты MAKE, необходимо просто соблюсти правила трансляции:
1. Проверять необходимость перекомпиляции по списку рутин, вхо­дящих в определенный проект.
2. Если есть INT рутина, но нет ее байткода, то транслировать.
3. Если есть MAC рутина но нет INT рутины и байткода, то транс­лировать.
4. Если дата изменения байткода раньше чем INT рутины или MAC рутины то транслировать.
5. Если MAC рутина зависит от какой-либо INC рутины и есть INC рутина с датой изменения позже чем MAC рутина, то транслиро­вать.
Кроме того, нужно определить по документации на используемую MUMPS систему, как именно можно получить дату и время последнего изменения INC, MAC, INT рутин и байткода, а также как именно про­граммно вызвать трансляцию MAC и INT рутин. Эти несколько правил
7.4. ПРЕПРОЦЕССОР 477
приведены навскидку и в реальном проекте и утилите перекомпиляции, конечно, могут учитываться более сложные правила, включая ведение возможных зависимостей MAC рутин от версии, от изменений управля­ющих данных в глобалах, и так далее.
При выполнении препроцессирования разработчик может использо­вать информацию о текущей версии MUMPS системы и, в зависимости от нее, управлять генерацией MUMPS кода. Например, директивами препроцессора определять, для какой MUMPS системы или операцион­ной системы выполняется трансляция, и использовать более удачные, или специфические, или оптимизированные для нее возможности. На­пример, генерация кода блокировки на чтение в зависимости от версии MUMPS системы:
#if $zv["MiniM" #define MINIM #else #define CACHE #endif
... ... ...
#ifdef MINIM
l +^A4("WH","MD","F",cubeId,$$LockName^ST51()):1
#else
l +(^A4("WH","MD","F",cubeId)#"S"):1
#endif
...
Здесь предполагается, что код может выполняться либо на системе MiniM, либо на системе Cach´e.
Поскольку рутины для макропроцессора различаются по типу, то к традиционным форматам экспорта рутин системы, поддерживающие пре­процессор, дополнительно поддерживают расширенный формат экспор­та рутин, сохраняющий информацию о типе рутины. Это формат RSA (Routine Save Archive).
MUMPS системы, поддерживающие препроцессор макросов, также, в принципе, могут быть использованы для кроссразработки для получения кода для целевой MUMPS системы, не поддерживающей препроцессор. Те места кода, которые должны отличаться в зависимости от версии, должны генерироваться по условию какую из целевых систем использо­вать. Например, перед трансляцией пакета рутин можно внести запись в глобал о версии и директивой #if проверить имя целевой системы.
Полученный пакет INT рутин затем может быть экспортирован в стандартном формате и перенесен на целевую MUMPS систему.
478 ГЛАВА 7. ПРАКТИКА ПРИМЕНЕНИЯ
Вариант кроссразработки под систему, не поддерживающую макро­сы, очень экзотичен и специфичен, а вот вариант с кроссразработкой под другую систему или под другую версию системы в практике встречает­ся чаще. Но традиционно, при необходимости иметь код, работающий на различных реализациях MUMPS, планируется иначе - либо выполнени­ем специфичного от версии кода через xecute, либо комплектованием прикладной системы набором специфичных для целевой системы рутин, либо, в крайнем случае, комбинированием соответствующих команд if или постусловий.

7.5 Формат $HOROLOG

Системная переменная $HOROLOG возвращает значение текущей даты и времени в локальном времени системы (с учетом часового пояса).
Дата и время возвращаются в виде двух чисел, разделенных запятой. Первое число - количество дней, прошедших начиная с пятницы, 31 декабря 1840 года. Второе число показывает количество секунд дня, прошедших с полуночи.
Формат и точку отсчета для переменной $HOROLOG выбрал James M. Poitras, один из разработчиков системы MDH, ранней версии совре­менных MUMPS систем, в 1969-м году. MDH - это крупная автомати­зированная система для учета медицинских сведений. С его слов:
- Я вспомнил, что старейший (видимо, наиболее старейший) граж­данин США, ветеран Первой Мировой Войны, имел к тому времени возраст 121 год. Поэтому я захотел представить дату в юлианском ка­лендаре так чтобы возраст можно было легко вычислять и представить любую дату в виде числа. Я решил, что начальной даты отсчета 1840 должно быть достаточно.
Другая легенда выбора отсчета 1840 года гласит, что в этом году
была произведена первая запись в систему MDH. Но это шутка.
В частности, отсчет 60000 приходится на 10 апреля 2005-го года.
Формат системной переменной $HOROLOG является наиболее рас­пространенным из всех используемых форматов дат и времени в при­кладных системах на MUMPS. С этим форматом работает множество расширенных $Z функций различных расширений, и дополнительные расширенные системные переменные, например $ZTIMESTAMP, также придерживаются выбранного формата.
При возврате значение переменной выглядит так:
USER>w $h 62542,57317
7.5. ФОРМАТ $HOROLOG 479
Разделитель запятая не должен при этом восприниматься как де­сятичная точка, это только разделитель количества дней и количества секунд. Число секунд не дополняется лидирующими нулями. Это обсто­ятельство необходимо учитывать при сортировке по дате и времени.
Для приведения формата $HOROLOG к виду, допускающему сорти­ровку, применяется или метод дополнения обоих чисел до строки или приведение к числу секунд. При дополнении до строки обе части допол­няются так, чтобы формат строки при строковой сортировке приводил к корректной сортировке дат, например такой:
USER>s h=$h w $j($p(h,","),6)_","_$j($p(h,",",2),6)
62542, 57693
При приведении к числу секунд часто используется два метода. Пер­вый основан на том, что в сутках содержится:
USER>w 24*60*60 86400
секунд, поэтому общее число секунд вычисляется по формуле:
USER>s h=$h w $p(h,",")*86400+$p(h,",",2) 5403686644
Для получения значений даты и времени в формате $HOROLOG из такого числа применяются операции деления нацело и взятие остатка от деления нацело на 86400.
Второй метод основан на условном числе секунд 100000 так, чтобы при вычислении в результате было видно обе части - и дата и время, каждая из частей в формате $HOROLOG:
USER>s h=$h s total=$p(h,",")*100000+$p(h,",",2)
USER>w h="62542,58032" total=6254258032
Для получения значений даты и времени в формате $HOROLOG об­ратно из такого числа применяется деление нацело и остаток от деления нацело на 100000.
Первый вариант приведения к числу (домножение номера даты на
86400), кроме того, используется для вычисления разности дат и време­ни в секундах для двух дат, заданных с указанием времени.
480 ГЛАВА 7. ПРАКТИКА ПРИМЕНЕНИЯ
Нужно обратить внимание на то, что значение $HOROLOG, как си­стемной переменной, волатильно, и два различных обращения к этой переменной могут дать не только различные значения секунд, но и раз­личные значения дат (при работе программы около полуночи). Поэтому на практике применяется взятие значения $HOROLOG однократно, а за­тем использование этого значения в нескольких операциях вычислений.
В настоящее время современные MUMPS системы строго поддержи­вают лишь положительный отсчет числа дней в $HOROLOG, но в слу­чае, если необходимо оперировать также отрицательными значениями, необходимо проверить, как эта операция поддерживается на применяе­мой и целевой MUMPS системах, или написать функции преобразования дат самостоятельно.
Нужно отметить, что корректное преобразование даты, заданной чис­лом относительно точки отсчета в номер года, месяца и дня, а также обратно, является не совсем тривиальной задачей, поскольку такая опе­рация должна учитывать, что каждый 4-й год високосный, при этом каждый 100-й не високосный, но каждый 400-й високосный.
В частности, на языке МUMPS один из вариантов декодирования значения даты в формате $HOROLOG в значения года, месяца и дня, может быть таким:
DATEDECO ; $horolog decoding to year, month, day
; MiniM internals ; http://www.minimdb.com q
IsLeapYear(Y)
q (Y#4=0)&((Y#100)!’(Y#400))
DECODE(H,Year,Month,Day) ; d DECODE^DATEDECO($H,.Y,.M,.D)
n D1,D4,D100,D400 s D1=365,D4=D1*4+1,D100=D4*25-1,D400=D100*4+1 n Y,M,D,I,T,DayTable s T=H+672046 ; return zeroes if date before 0 year i T’>0 s Year=0,Month=0,Day=0 q s T=T-1,Y=1 f q:T<D400 s T=T-D400,Y=Y+400 s I=T\D100,D=T#D100 i I=4 s I=I-1,D=D+D100 s Y=Y+(I*100),I=D\D4,D=D#D4,Y=Y+(I*4),I=D\D1,D=D#D1 i I=4 s I=I-1,D=D+D1 s Y=Y+I,DayTable="31,28,31,30,31,30,31,31,30,31,30,31" i $$IsLeapYear(Y) s $p(DayTable,",",2)=29 s M=1 f s I=$p(DayTable,",",M) q:D<I s D=D-I,M=M+1 ; return decoded day