Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
MUMPS СУБД. Практика применения и опыт программирования.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
2 Мб
Скачать
1.1. КОМАНДЫ 31
Нужно отметить, что при выполнении тела цикла сама переменная цикла может принять и неопределенное значение при выполнении ко­манд kill или new. В этом случае при очередной итерации цикла произ­водится ее чтение и генерируется не ошибка доступа к неопределенной переменной (код M6), а специально зарезервированная ошибка доступа к неопределенной переменной цикла (код M15).
Для трехаргументной формы for условие окончания итерации прове­ряется каждый раз перед выполнением команд цикла. При этом команда поступает в зависимости от знака приращения итерации - если прира­щение положительное, то цикл прекращается если значение переменной цикла стало больше конца цикла и если приращение отрицательное, то цикл прекращается если значение переменной цикла стало меньше кон­ца цикла. В целом, синтаксис команды for в трехаргументной форме определен наиболее ожидаемым для разработчиков образом.
Интересно, что для двухаргументной и трехаргументной формы па­раметры цикла (приращение и окончание) командой for вычисляются строго однократно, перед первым выполнением цикла и далее изменение значений входящих в эти выражения, не влияют на выполнение for ­команда будет строго пользоваться вычисленными первоначально значе­ниями.
Одной из наиболее интересных особенностей команды for в языке MUMPS является возможность указать несколько аргументов, как было упомянуто ранее. Аргументы применяются последовательно к перемен­ной цикла, один за другим. При этом разные аргументы могут иметь разную форму - одноаргументную, двухаргументную и трехаргументную. Пример трех аргументов для команды for, и все три в одноаргументной форме:
USER>for i="abc","def","qwe" w i,! abc def qwe
USER>
Вариант комбинирования различных форм:
USER>for i="abc","def",1:1:5,"qwe" w i,! abc def 1 2 3
32 ГЛАВА 1. СРЕДА ИСПОЛНЕНИЯ
4 5 qwe
USER>
Возможность указать подряд несколько одноаргументных форм через запятую иногда приводит к тому, что разработчики считают это отдель­ной формой команды for, выполняющей итерации по перечисленному списку:
USER>for i="abc","def","qwe" w i,! abc def qwe
USER>
На самом деле это не так, это несколько одноаргументных форм, и вместе с ними возможно смешивание любых других форм команды for кроме, конечно, вечного цикла.
Что еще интересно, все три условные команды языка MUMPS, if, else и for, являются строго стандартными, различные реализации языка не отклоняются от стандартного их определения и к ним не были добавлены другие условные команды.
Кроме того, что команда for может быть прервана безаргументной ко­мандой quit или генерацией ошибки, нормальный ход выполнения цикла, естественно, может быть прерван безусловным переходом к явно ука­занной строке. В этом случае исполняющая система MUMPS отменяет текущий контекст выполняемых циклов и выполнение команд продол­жается вне контекста цикла. Разумеется, после безусловного перехода по goto никакие итерации в текущем контексте более не имеют смыс­ла, но сохраняются изменения локальных переменных, использованных в качестве переменных цикла.
Это общее правило для любых языков програмирования, допускаю­щих безусловный переход в контексте цикла, за исключением перехода в тот же контекст. Но для строчно-ориентированных интерпретаторов понятие перехода в тот же контекст цикла не поддерживается.
В отношении условных команд (if, else, for) действует специальное синтаксическое правило языка - для них не допускается использование постусловий. Кроме того, для команды for синтаксически не допуска­ется косвенное задание аргумента. Косвенность допускается лишь для вычисления имени переменной цикла, начального значения, приращения
1.1. КОМАНДЫ 33
и условия окончания итерации. Задание же структуры цикла косвенно не поддерживается.
Стандарт на язык в отношении команд цикла for предусматривает, что в качестве переменных цикла могут быть использованы исключитель­но локальные переменные. Автор пока не встретил веских оснований, почему бы в качестве переменной цикла не использовать глобальную переменную. Формально, для виртуальной машины при исполнении ко­манды for нет существенных отличий, с какой именно переменной надо выполнить действия, с локальной или глобальной. Но в настоящее время язык определен именно таким образом, и все MUMPS системы запре­щают использовать в качестве переменной цикла что-либо иное, кроме локальной переменной.
Этот запрет поддерживается даже если переменная цикла задана кос­венно. Такой код
USER>f ^i=1:1:2
приведет к синтаксической ошибке времени трансляции, а такой
USER>s n="^i"
USER>f @n=1:1:2
к синтаксической ошибке времени выполнения.
Но язык не ограничивает множество имен для переменной цикла только неиндексированными локальными переменными, и разработчи­ки могут свободно использовать также индексированные локальные пе­ременные. При этом все MUMPS системы вычисляют имя переменной цикла (даже если в качестве индексов указаны волатильные выражения) однократно, и далее используют только его.
То же самое правило об однократном вычислении (при начале вы­полнения цикла) распространяется на начальное значение, приращение и значение окончания цикла. Даже если они заданы волатильными вы­ражениями, команда for будет использовать только те значения, которые были получены при их первом вычислении.

1.1.3 Команды передачи управления

Для изменения последовательного хода программы используются коман­ды передачи управления. К таким относятся команды, действие которых приводит к смене текущей строки исполнения без возврата или с воз­вратом управления на следующую команду.
34 ГЛАВА 1. СРЕДА ИСПОЛНЕНИЯ
В целом, все, что исполняет MUMPS система, задается либо совокуп­ностью имеющихся рутин, либо интерактивно введенной строкой, либо косвенным исполнением. Каждая из рутин представляет собой текст, рассматриваемый как последовательность строк. В принципе, для строч­но - ориентированной машины исполнения не имеет значения, на какую из других имеющихся строк (в этой же рутине или в другой) передать управление.
Для того чтобы можно было указать строку рутины, есть в любом случае наиболее простой способ - это указать ее номер, например рутина ˆABC, строка 14.
Но такой способ неудобен в случае если рутины модифицирутся и но­мера строк могут измениться со временем. Для того чтобы указать опре­деленную строку, ей приписывается так называемая метка. В MUMPS для описания метки не требуется никаких деклараций или отдельного синтаксиса - просто если строка рутины начинается не с пробельного символа, то первое слово строки и есть метка.
При исполнении строки с меткой сама метка не содержит никаких указаний исполняющей системе MUMPS за исключением того, что име­нует строку, в которой она находится.
Для того чтобы передать управление на другую строку, нужно ука­зать рутину (можно не указывать если это текущая рутина), метку, либо смещение относительно начала рутины, либо метку плюс смещение от­носительно метки.
Механизм использования меток в MUMPS используется и для орга­низации безусловного перехода и для перехода на подпрограмму с воз­вратом из нее. Одна и та же метка может быть использована в обоих случаях. Отношение к метке и ее значение определяется в отличие от других языков не тем, как она описана, а тем, как она вызвана. При трансляции рутины транслятор еще не знает, как будет использована строка с меткой, поэтому готов выполнить вызов любой строки, обеими вариантами.
Собственно, самих подпрограмм в понимании других языков про­граммирования, как самостоятельной цельной единицы трансляции, в MUMPS не существует. Будут ли строки рутины работать как просто последовательный набор строк или как подпрограмма, определяет раз­работчик.
Автору доводилось работать с прикладными системами, написанными на MUMPS, часть рутин которых организована в виде логически свя­занного списка. Каждая из рутин (кроме последней) в последней своей строке содержит команду безусловного перехода на первую строку сле­дующей рутины. В итоге весьма большое количество кода выполняется
1.1. КОМАНДЫ 35
последовательно. При разработке реально больших систем, где даже с применением столь компактного языка, как MUMPS, объем кода может оказаться очень большим, и такие возможности языка были использова­ны весьма грамотно. В любом случае, язык MUMPS не будет навязывать какой-либо стиль и лишать каких-либо возможностей.
Если после имени метки разработчик ставит в круглых скобках пе­речень аргументов через запятую, то такая метка становится меткой с параметрами и ее надо вызывать строго как подпрограмму, с передачей параметров. При вызове исполняющая система MUMPS сопоставляет переданные фактически параметры объявленным формальным парамет­рам таким образом, что следующие после метки команды (неважно в этой же строке или в следующей) уже исполняются в контексте, в кото­ром эти формальные параметры являются локальными переменными.
Если метка была объявлена с параметрами, то на нее нельзя перей­ти безусловным переходом, поскольку исполняющая машина (интерпре­татор) MUMPS не знает как и что сопоставить этим параметрам как локальным переменным нового контекста, при безусловном переходе не создается новый контекст или фрейм на стеке.
К командам, передающим управление, в языке MUMPS относятся:
1. HALT - команда останавливает процесс, после выполнения этой команды процесс завершает работу и далее никакие команды не выполняются.
2. DO - управление передается либо на указанную метку как на под­программу, либо на последующий блок строк.
3. QUIT - управление возвращается из подпрограммы либо прерыва­ется выполнение цикла.
4. XECUTE - команда выполняет свой аргумент как последователь­ность строк.
5. GOTO - управление передается безусловным переходом на указан­ную строку.
Команда HALT (H) останавливает процесс полностью, все локаль­ные переменные процесса пропадают, и память занятая процессом осво­бождается. Все устройства ввода-вывода, которые были открыты закры­ваются, и все блокировки, установленные процессом, снимаются. Если команда выполнилась, то дальнейшие команды в этой или последующей строке не выполняются.
36 ГЛАВА 1. СРЕДА ИСПОЛНЕНИЯ
Команда DO (D) имеет две формы - можно указать метку в рутине (с возможным смещением) для вызова в качестве подпрограммы либо безаргументная форма, при которой последующий блок строк рассмат­ривается как подпрограмма. Например
do label^rtn
передает управление на рутину rtn начиная с метки label, и в новом контексте создается стековый фрейм. Если в вызванном коде будет вы­полнена команда quit, то эта команда вернет управление на команду, следующую после команды do, обратившейся к метке. В этом случае вызов соответствует вызову подпрограммы или процедуры.
Чтобы передать параметры подпрограмме, нужно чтобы вызываемая метка принимала параметры и чтобы вызывающий код передал их. Па­раметры в обоих местах указываются в круглых скобках. Если есть под­программа
label(param)
write "param = ",param,! quit
то ее можно вызвать с передачей параметров:
do label^rtn(789)
В вызываемом коде команды следующей в строке после метки уже работают в контексте нового стекового фрейма и параметр param уже яв­ляется локальной переменной с областью видимости от текущего уровня стека и далее.
Если подпрограмма была вызвана командой DO, то от подпрограммы не ожидается возврат значения и команда QUIT должна быть исполь­зована в безаргументной форме, иначе при выполнении кода система генерирует ошибку. Если требуется вызов подпрограммы с возвраща­емым значением, то для возврата значения должна быть использована аргументная форма команды quit и метка должна быть вызвана как поль­зовательская функция, с указанием символов $$ перед именем метки. В этом случае метку можно использовать как часть вычисляемого выра­жения произвольной сложности. Например, функция вычисляющая куб числа:
CUBE(value)
quit value*value*value
должна быть вызвана с указанием вызова пользовательской функции:
1.1. КОМАНДЫ 37
set result=123+$$CUBE^rtn(789)+456
Если подпрограмма вызывалась как пользовательская функция с ожи­данием возврата, то управление из нее должно возвращаться командой quit с аргументом. Значение аргумента вычисляется как выражение и возвращается в место вызова. Если такая функция прерывается и управ­ление возвращается безаргументной формой команды quit, то система генерирует ошибку.
В других языках есть возможность указать неименованные части функции как блок операций, исполняемых цельно, например в языке C++ все что ограничено фигурными скобками исполняется как единый последовательный контекст:
funcname( int param) {
// code block 0 ... if(param == 2) {
// code block 1 ... for(int i = 1; i < 4; i++) {
// code block 2 ...
} } else {
// code block 3
... } return;
}
В языке MUMPS, как в строчно - ориентированном, нет возможности указать границы блока при трансляции текста рутины. В других анало­гичных строчно - ориентированных языках могут быть использованы, например, синтаксические конструкции NEXT, являющиеся не столько командами, сколько операторными скобками ораничения блока команд или операторов.
В языке MUMPS, как строчно-ориентированном, вместо этого приме­няется специальный маркер в начале строки, символ точка. Если строка начинается с точки, это означает что строки с равным числом точек в начале, и идущие последовательно, образуют один уровень вложения
38 ГЛАВА 1. СРЕДА ИСПОЛНЕНИЯ
блока строк. Если нужно организовать блок в блоке, то ставится две точки, и так далее. При использовании безаргументной формы команды DO интерпретатор переходит в режим перехода от строки к строке с учетом числа точек в начале строки. Если начали выполнять от уровня с числом точек N, то все последующие строки идущие подряд с числом точек N + 1 считаются принадлежащими блоку. Как только интерпре­татор встречает строку у которой число точек меньше или равно N это означает что выполняемый блок строк закончен. Если при этом интер­претатор встречает строки с числом точек больше N + 1, то они просто пропускаются. Например
write "begin",! if a=1 do . write "in block",! . s a=0 write "end",!
Здесь две строки
. write "in block",! . s a=0
образуют неименованный блок строк, выполняемый безаргументной фор­мой команды do как подпрограмма. При переходе от строки
. s a=0
к следующей интерпретатор обнаруживает, что число точек стало мень­ше чем у выполняемого блока и выполняет неявную команду quit в без­аргументной форме, и управление возвращается к команде, следущей за командой do. В строке после команды do в данном случае ничего не указано, и интерпретатор переходит к следующей строке. Но в ней число точек больше чем число точек текущего контекста (0), поэтому обе строки с точками просто пропускаются и интерпретатор переходит непосредственно к выполнению команды после строк с точками
write "end",!
После команды do в этом примере также могут стоять команды. Нуж­но лишь учесть, что для вызова блока строк как подпрограммы нужна именно безаргументаня форма команды DO, поэтому необходимо два пробела:
1.1. КОМАНДЫ 39
write "begin",! if a=1 do write "after do",! . write "in block",! . s a=0 write "end",!
Нужно отметить, что в системе Cache старших версий поддерживает­ся расширенный синтаксис, который включает как свободное формати­рование пробелов по контексту, допускающем их, так и применение фи­гурных скобок для ограничения области действия синтаксических кон­струкций. В частности, безаргументная команда DO и некоторые другие в этих условиях могут быть применены не к последовательности строк с точками, а к поледовательности команд, ограниченной фигурными скоб­ками.
Команда GOTO (G) выполняет просто переход. Исполняющая систе­ма MUMPS начинает выполнять первую команду находящуюся на ука­занной строке. Строка может иметь метку, но не может иметь у метки параметры. Если при выполнении команды GOTO действовал контекст цикла, то он отменяется. Контекст стека полностью сохраняется. Вызы­ваемый код может вызвать команду quit, которая вернет управление.
В отношении команды перехода GOTO язык MUMPS не имеет огра­ничений как в других языках. Например, в языке C или Pascal переход возможен только в пределах той же самой функции, в языке Assembler переход возможен на метку только в пределах команд ассемблерного мо­дуля, но не в другой ассемблерный модуль, ассемблерные вставки в коде C / C++ могут как допускать, так и не допускать переход ассемблерной командой в контекст другой функции в зависимости от транслятора. В случае с MUMPS свобода перехода полная, включая возможность вы­числять смещение как выражение и применять косвенные формы фраг­ментов метки.
Для перехода действует только одно единственное ограничение - сме­щение относительно метки, если оно указано, не должно быть отрица­тельным числом, таковы требования стандарта языка MUMPS.
Примеры команд GOTO:
; Routine R01
func(param)
s value=123 i +param goto param+param^R02
continue
w value ...
40 ГЛАВА 1. СРЕДА ИСПОЛНЕНИЯ
; Routine R02 param 1 w "handler of 1",! goto continue^R01 2 w "handler of 2",! goto continue^R01 3 w "handler of 3",! goto continue^R01 ...
Как и положено в компьютерной литературе, этот пример мало похож на более-менее осмысленный, но показывает примерные возможности ор­ганизации переходов. Из текста первой рутины производится переход к строкам второй, и обратно из второй к строке в первой. При этом можно вычислять смещение для строки перехода. В синтаксической конструк­ции
goto param+param^R02
goto означает команду, первое вхождение param означает имя метки, второе вхождение param означает величину смещения в строках относи­тельно метки, и R01 означает имя рутины, в которой эти строки нужно найти.
Кроме того, что в языке MUMPS можно организовывать переходы произвольным образом, добавлением постусловий как для команды, так и для аргумента, можно организовывать также переходы по условию. В частности, следующие синтаксические конструкции не в точности, но приблизительно эквивалентны, за исключением взведения системной переменной $TEST:
i +param goto param+param^R02 g:param param+param^R02 g param+param^R02:param
Команда XECUTE (X) имеет только аргументную форму, и при вы­полнении команды значение аргумента используется как последователь­ность команд. Выполнение команды xecute приводит к выполнению ко­манд из аргумента как если бы в текущей рутине существовала метка с именем не равным ни одной имеющейся метке:
uniqlabel
xecutearg quit
Перед непосредственно выполнением команд, входящих в аргумент, команда XECUTE создает новый стековый фрейм. После выполнения