Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
MUMPS СУБД. Практика применения и опыт программирования.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
2 Мб
Скачать
1.13. КОСВЕННОСТЬ 151
check(value)
d @$s(value#2:"odd",1:"even") q
even
w "subroutine for even values",! q
odd
w "subroutine for odd values",! q
Здесь, в зависимости от того, является значение value четным или
нечетным, вызываются различные подпрограммы.
Косвенность метки позволяет разработчикам на MUMPS, в отличие от многих других языков программирования, использовать вычисляемый GOTO, когда метка для перехода вычисляется выражением или подпро­граммой произвольной сложности, включая использование настроек в глобалах, и результат в силу позднего связывания системы выполне­ния может указывать куда угодно. В случае если в действительности к моменту выполнения такой метки не найдено, система выполнения гене­рирует ошибку.
В частности, такой обобщенный механизм может использоваться в системах, выполняющих проверку прав пользователей, выполнения ко­да в зависимости от типа применяемой MUMPS системы, выполнения меток, передаваемых от клиентской программы, и во многих других за­дачах.
Косвенность метки может быть использована в любом месте, где син­таксически по контексту ожидается метка. Вычисление значения проис­ходит перед использованием метки. В случае если само выражение так­же содержит операторы косвенности, то выражение продолжает развер­тываться до тех пор, пока не будет развернуто и вычислено полностью. После этого значение проверяется синтаксически и MUMPS система ис­пользует соответствующие указанные в значении фрагменты метки.

1.13.4 Косвенность аргумента

Косвенностью аргумента команды называется применение команды к вы­численному содержанию выражения. Сама форма косвенности в свою очередь также является допустимым аргументом, поэтому язык MUMPS допускает синтаксическую рекурсию косвенности аргументов произволь­ной сложности.
При применении команды к косвенно заданному аргументу сначала вычисляется выражение, и затем его значение используется как аргумент
152 ГЛАВА 1. СРЕДА ИСПОЛНЕНИЯ
команды. В значении могут быть указаны через запятую последователь­ность нескольких аргументов, в этом случае команда применяется к ним последовательно слева направо так же, как если бы они были указаны явно.
Примеры косвенного указания аргумента команды:
s arg="#,123,!" w @arg w @(^Settings(123)) s if="count,total" i @if ...
Все стандартные команды языка поддерживают косвенность аргумен­та кроме команды FOR. Кроме того, производители MUMPS систем под­держивают дух MUMPS с возможностью задать аргумент косвенно в том числе и для расширенных дополнительных команд.
Например, многие MUMPS системы поддерживают семейство команд ZLOAD / ZSAVE / ZREMOVE, в аргументе которых указывается отдель­ная синтаксическая конструкция, отсутствующая в определении стан­дартного языка - имя рутины, соответствующее фрагменту определения метки. Например:
ZLOAD EXTEN2 ZSAVE VIEW5
Здесь идентификаторы EXTEN2 и VIEW5 MUMPS система воспри­нимает по контесту, как имена рутин. Чтобы передать командам имя ру­тины, которое содержится в переменной, нужно применить косвенность аргумента команд ZLOAD и ZSAVE:
s name="EXTEN2" ZLOAD @name s name="VIEW5" ZSAVE @name
Общее правило развертывания косвенностей аргументов определено в таком виде, что является некоторым (не полным) аналогом команде XECUTE со скрытым специальным конструированием аргумента путем добавления имени команды, например некоторым аналогом может быть замена
w @arg -> x "w "_arg s @arg -> x "s "_arg
1.13. КОСВЕННОСТЬ 153
Многие реализации MUMPS систем для снижения трудоемкости вы­полнения системы именно таким способом и воспользовались. В случае если применяемая MUMPS система выполнена именно так, то нужно быть очень внимательным к ее реальному поведению и обязательно про­верить соответствуют ли MUMPS система задачам которые необходимо выполнить. Отличие косвенности аргумента от выполнения тех же дей­ствий командой XECUTE состоит в проверке допустимости синтаксиса как аргумента именно этой команды, в создании командой XECUTE до­полнительного уровня стека и в характере диагностики в случае ошибки.
Как это проверить - нужно подставить в аргумент команды, переда­ваемый косвенно, кроме синтаксически корректного для этой команды аргумента, дополнительно пробел и другую команду, или несколько, с возможными аргументами. Согласно стандарта языка, MUMPS систе­ма должна отказаться выполнять такой аргумент команды, поскольку он не соответствует команде синтаксически. Во-вторых, такая переда­ча дополнительных команд случайно выполнить недопустимый с точки зрения безопасности приложения код. И, в-третьих, даже при передаче безопасного кода система может переключиться в некорректное состоя­ние, например изменить область видимости переменных и нарушить не данные, а ход выполнения процесса.
Кроме того, при выполнении такого синтаксически некорректного ар­гумента MUMPS система может дать диагностическое сообщение об ошибке, реально соответствующее лишь внутреннему состоянию эмуля­тора, не указывая на реальную ошибку.
Другим пунктом, который может потребовать проверки соответству­ют ли применяемая система и код на MUMPS который предстоит ей выполнять, является возможность вложений косвенности. Случай с бес­конечной рекурсией, очевидно, не может быть выполнен в техническом отношении в конечном итоге, независимо от архитектуры MUMPS си­стемы, но какое-то количество уровней косвенности может понадобиться для приложения. Для проверки этого пункта нужно проверить, коррект­но ли MUMPS система выполняет необходимые вложения сочетаний, используемых видов косвенностей, и аргументов команды XECUTE.

1.13.5 Косвенность шаблона

Косвенность шаблона является, видимо, наиболее простой формой кос­венности. В этом случае вычисляемое выражение, к которому применен оператор косвенности, трактуется как шаблон. Например:
USER>s pat="1E"
154 ГЛАВА 1. СРЕДА ИСПОЛНЕНИЯ
USER>w 1?@pat 1 USER>w 12?@pat 0
При этом, в отличие от метки, косвенность в шаблонах синтаксически не допускается для отдельных его фрагментов, шаблон всегда должен быть задан целиком либо по месту применения, либо целиком в виде результата вычисления выражения.
Операторы косвенности вместе с командой XECUTE образуют основ­ное препятствие для построения MUMPS системы в виде строго ком­пилирующей системы, работающей с кодом, точный алгоритм которого может быть определен на этапе трансляции.
При этом косвенность образует очень сильное средство для построе­ния модулей инструментального характера, модулей с обобщенными ал­горитмами, с развитыми настройками. Механизм косвенности MUMPS дает намного больше возможностей, чем другие языки, включая методы, не применимые в других языках.
Как иллюстративный пример можно привести построение систем, ориентированных на консольный алфавитно-цифровой ввод-вывод. Раз­личные терминальные устройства могут иметь различные последователь­ности операций, которые необходимо выполнять для одних и тех же действий. Для таких терминалов можно вести глобал с настройками, в которых хранятся выполняемые для этих терминалов команды или ар­гументы команд. Для добавления еще одного типа терминала в этом случае будет достаточно добавить соответствующие ему настройки.
Как еще одну иллюстрацию можно привести применение функций с обобщенными алгоритмами, которые описывают непосредственно сам алгоритм, но с использованием косвенности могут быть применены к самым различным данным. В частности, к обобщенным алгоритмам от­носятся операции на индексных структурах.

1.14 Интерпретатор

Как уже было описано в разделе косвенности, язык MUMPS содержит определения синтаксических конструкций, не позволяющих выполнить систему выполнения кода по схеме традиционных компиляторов с од­ним только неизменяемым исполняемым кодом. Этому препятствуют и операции косвенности, и команда XECUTE.
Все имеющиеся реализации MUMPS систем в той или иной мере яв­ляются интерпретаторами. Это либо интерпретаторы интерпретирующего
1.14. ИНТЕРПРЕТАТОР 155
типа или интерпретаторы компилирующего типа или комбинированные.
Интерпретаторы интерпретирующего типа получают на входе стро­ку, разбирают ее и выполняют. Интерпретаторы компилирующего типа получают исходный код и транслируют в промежуточный байткод, ко­торый уже может быть исполнен виртуальной машиной интерпретатора. Интерпретаторы комбинированного типа сочетают эти две возможности, например весь код может быть странслирован в команды процессора, но при выполнении косвенности или команды XECUTE система выполнения переходит к режиму интерпретатора, или одна строка может целиком транслироваться в байткод, выполняться, но полученный промежуточ­ный байткод не сохраняется для дальнейшего использования.
Большинство современных реализаций MUMPS систем строятся как интерпретаторы компилирующего типа, включая генерацию промежуточ­ного нехранимого байткода для команды XECUTE. Такой режим работы обеспечивает наибольшую скорость работы в сочетании с соблюдением требований языка, или наилучший баланс возможности / производитель­ность.
При генерации байткода разработчики различных систем использу­ют различные собственные определения состояний виртуальной маши­ны исполнения, коды, схему преобразований, форматы компилированных рутин.
В укрупненном виде байткод, получаемый трансляцией рутины, со­стоит из нескольких секций - секция заголовка, описывающая размеще­ние других секций, секция констант, секция строк помещенных целиком, секция списка меток, секция последовательности байткодов строк и дру­гие, в зависимости от реализации.
Основная схема работы интерпретатора строится на модели виртуаль­ной машины - регистровая или стековая. Определение языка MUMPS наиболе близко к определению стековой машины. Для такой машины ис­полнения кроме обычного стека фреймов для работы подпрограмм под­держивается стек вычисления. Это внутренняя структура, недоступная для языка.
Для стека вычислений поддерживаются операции положить на стек значение, целое число, метку или имя, и взять со стека значение, целое число, метку или имя. Определение значения может варьироваться так­же в зависимости от реализации MUMPS системы, например это может быть структура содержащая тип значения и само значение кодированное в соответствии с используемым типом.
Для всего набора элементарных действий для виртуальной машины определяются коды операций. Например, пусть будет определена вирту­альная машина с кодами операций:
156 ГЛАВА 1. СРЕДА ИСПОЛНЕНИЯ
Код операции Действие op_const Взять следующий байт и, считая его номером кон-
станты, взять из секции констант значение и поме­стить его на стек.
op_plus Взять со стека два значения, сложить и поместить
результат на стек.
op_mult Взять со стека два значения, умножить и поместить
результат на стек.
op_horolog Вычислить значение даты и времени в формате
$HOROLOG и поместить значение на стек.
op_concat Взять со стека два значения, выполнить конкатена-
цию и поместить результат на стек.
op_writestr Взять со стека значение, привести к строке и вывести
в текущее устройство. op_writenl Вывести в текущее устройство перевод строки. op_lenght_2 Взять со стека значение, привести к строке, вычис-
лить длину и результат поместить на стек как значе-
ние.
Используя такую виртуальную машину, можно подать ей на трансля-
цию и затем на выполнение уже несколько заданий:
1) write $h,!
2) write 12+45*78,!
3) write 123_456_789,!
При трансляции строки
write $h,!
получаем последовательность байткодов:
1) op_horolog
2) op_writestr
3) op_writenl
При их последовательносм выполнении сначала вычисляется значе­ние $H, заносится на стек, затем значение вынимается со стека и вы­водится в текущее устройство, затем в текущее устройство выводится перевод строки.
При трансляции строки
write 12+45*78,!
1.14. ИНТЕРПРЕТАТОР 157
получаем последовательность байткодов:
1) op_const
2) 1
3) op_const
4) 2
5) op_plus
6) op_const
7) 3
8) op_mult
9) op_writestr
10)op_writenl
и соответствующую ей таблицу констант:
1 12 2 45 3 78
При их последовательном выполнении виртуальная машина выполня-
ет следующие действия при пустом начальном стеке вычисления:
1. op_const, 1: Берет константу номер 1 (значение по таблице = 12) и помещает на стек. Состояние стека
12
2. op_const, 2: Берет константу номер 2 (значение по таблице = 45) и помещает на стек. Состояние стека
45 12
3. op_plus: Берет со стека два значения, состояние стека пусто (в дан­ном примере записи на стеке кончились). Вычисляет сумму (полу­чается 57) и помещает значение на стек, состояние стека
57
4. op_const, 3: Берет константу номер 3 (значение по таблице = 78) и помещает на стек. Состояние стека
78 57
5. op_mult: Берет со стека два значения, состояние стека пусто (в данном примере записи на стеке кончились). Вычисляет произве­дение (получается 4446) и помещает значение на стек, состояние стека
158 ГЛАВА 1. СРЕДА ИСПОЛНЕНИЯ
4446
6. op_writestr: Берет со стека значение, состояние стека - пусто. Зна­чение приводится к строке и выводится в текущее устройство. Со­стояние стека - пусто.
7. op_writenl: Выводит в текущее устройство перевод строки, состоя­ние стека - пусто.
Третий пример транслируется соответственно в последовательность
байткодов:
1) op_const
2) 1
3) op_const
4) 2
5) op_concat
6) op_const
7) 3
8) op_concat
9) op_writestr
10)op_writenl
и соответствующую таблицу констант:
1 123 2 456 3 789
Соответственно, при последовательном выполнении такого байткода
со стеком вычисления производятся операции:
1. На стек помещается значение 123.
2. На стек помещается значение 456.
3. Со стека снимаются два значения, стек пуст.
4. На стек помещается значение 123456.
5. На стек помещается значение 789.
6. Со стека снимается два значения, стек пуст.
7. На стек помещается значение 123456789.
8. Со стека снимается значение.
1.14. ИНТЕРПРЕТАТОР 159
Последнее снятое со стека значение выводится в текущее устройство,
и следом выводится символ перевода строки.
Примерно по такой схеме выполняется большинство стековых вир­туальных машин. Реальная виртуальная машина дополнительно поддер­живает более сложные записи на стек, такие как вычисленная метка для выполнения перехода или вызова подпрограммы, и вычисленное имя для взятия значения переменной или для записи.
Реальная виртуальная машина должна будет поддерживать не опера­цию взять со стека значение, а взять со стека то что там есть и по нему вычислить значение. В частности, там может оказаться не само значе­ние, а имя переменной или индикатор голой ссылки. При этом для мно­жества команд и функций требуется передавать не значения, а именно вычисленные имена переменных, например для функции $INCREMENT, $NAME или $ORDER.
В определении языка MUMPS описаны несколько функций, которые поддерживают несколько форм, например функции $GET, $PIECE. Для них виртуальная машина должна сгенерировать код исполнения или на каждую поддерживаемую форму, или единый байткод для все форм, но для сокращенных вариантов дополнительно генерировать код вычисле­ния значения по умолчанию.
Структурно система исполнения виртуальной машины интерпретато­ра состоит из цикла выборки следующего байткода, помещения на стек и взятия со стека значений, имен, констант, и набора собственно исполня­ющих функций, выполняющих непосредственно операции со значениями и именами, например функции сложения, конкатенации, вычисления $H, вывода значения в текущее устройство.
У каждой виртуальной машины, если она не стандартизирована, опре­деление таких операций собственное. Существуют относительно стан­дартизированные виртуальные машины исполнения интерпретаторов, на­пример виртуальные машины Java, .NET, Lua. Для таких машин воз­можна трансляция других языков программирования в их байткод для того чтобы эти виртуальные машины использовались для выполнения как готовые. Для систем выполнения MUMPS такие стандарты на фор­мат исполняемых бйткодов официально не поддерживаются и построе­ние системы исполнения полностью определяет производитель MUMPS системы.
Сам генератор байткода по исходному тексту опирается на определе­ние языка, данное для каждой синтаксической конструкции. Рассмотрим парсинг с кодогенерацией на примере разбора выражения. Выражение в языке MUMPS определено так:
160 ГЛАВА 1. СРЕДА ИСПОЛНЕНИЯ
expr := expratom [exprtail]
Это означает что для парсинга выражения (expr) необходимо снача­ла выполнить парсинг expratom, а затем, если есть символы, то парсинг exprtail. В свою очередь, определение expratom состоит из двух альтер­натив:
expratom := | glvn |
| expritem |
Здесь glvn - определение синтаксической единицы глобальной или локальной переменной, expritem - самостоятельная синтаксическая еди­ница.
При наличии альтернатив парсер выполняет просмотр дальнейшего текста на предмет соответствует ли дальнейший текст определению пер­вой альтернативы. При этом может выполняться также параллельная кодогенерация для вычисления этой альтернативы. Если кодогенерация альтернативы не получилась, то парсер восстанавливает свое состояние на момент начала разбора альтернативы и переходит к следующей. И так далее, пока не кончатся альтернативы.
Если ни одна альтернатива не подошла, то парсер возвращает на предыдущий уровень парсинга состояние неудачи парсинга. Если на ка­ком - либо уровне парсинга транслятор может принять решение о том, что отсутствие альтернатив парсинга означает синтаксическую ошибку, то может вывести сообщение о синтаксической ошибке. В большинстве случаев это касается неудачи разбора последней альтернативы.
В рассматриваемом примере определение expritem состоит из перечня более детальных альтернатив, соответствующих вычислению значений:
expritem := | strlit | константная строка
| numlit | константное число | exfunc | пользовательская функция | exvar | пользовательская переменная | svn | системная переменная | function | системная функция | unaryop expratom | унарный оператор с
последующим expratom
| (expr) | открывающая круглая скобка,
последующий элемент в определении expr, с последующей закрывающей круглой скобкой