Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
MUMPS СУБД. Практика применения и опыт программирования.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
2 Мб
Скачать
5.3. ETRAP 371

5.3 ETRAP

Обработчик ошибок ETRAP был определен в стандарте языка MUMPS ANSI 1995-го года и в настоящее время входит в ныне действующий стандарт ISO M UMPS. В существенной степени его функционал был выбран похожим на наиболее сильные стороны обработчика ZTRAP, ре­ализованного в системе GT.M.

5.3.1 Определение

Основу обработчика ETRAP составляет комплекс следующих соглаше­ний:
1. Схема обработки ошибок стандартизирована и переносима.
2. Обработчик задается не меткой, а строкой команд, которая выпол­няется при переходе процесса в состояние ошибки.
3. Имеется возможность изменять установленный ранее обработчик $ETRAP.
4. Имеется возможность указать явно область видимости действия обработчика $ETRAP командой NEW.
5. Имеется возможность вести независимый отсчет по стеку систем­ной переменной $ESTACK, применяя по необходимости к ней ко­манду NEW.
6. Имеется системная переменная $STACK и парная ей системная функция $STACK() для анализа стека.
7. Имеется системная переменная $ECODE для чтения текущего со­стояния ошибки, для генерации ошибки при присваивании непу­стой строки и для окончания обработки ошибки при присваивании пустой строке.
В целом, весь этот комплекс соглашений реализует более мощную и гибкую схему обработки ошибок, чем простые правила обработчиков ZTRAP, связанные с неявной передачей управления командой GOTO на метку.
Алгоритм выполнения обработчика ошибок по схеме ETRAP, вообще говоря, стандартен, но, поскольку в каждой из MUMPS систем присут­ствует в той или иной мере схема обработки ZTRAP, в каждой из них
372 ГЛАВА 5. ОБРАБОТКА ОШИБОК
могут быть отдельные нюансы по сочетанию двух этих обработчиков, по приоритету их действия в состоянии ошибки, и по переключению про­цесса в зависимости от последнего присваивания обработчика - ETRAP или ZTRAP. Далее будет рассматриваться схема чистого обработчика ETRAP, в состоянии, когда процесс не определял обработку по ZTRAP. В случае, если прикладная программа сочетает оба варианта, програм­мистам необходимо сверить по документации на используемую MUMPS систему характер обработки ошибок для применяемой MUMPS системы, включая возможные особенности ее версии.

5.3.2 $ETRAP

Для описания действий, которые необходимо предпринять при обработке ошибки, используется присваивание специальной системной переменной $ETRAP последовательности команд.
При этом системная переменная $ETRAP может быть ограничена по области действия командой NEW. При присваивании выполняется изме­нение значения $ETRAP на том уровне стека, где к $ETRAP послед­ний раз была применена команда NEW, а если не было, то изменяется ее значение на нулевом, начальном уровне стека. При чтении перемен­ной $ETRAP возвращается значение присвоенное на последнем уровне стека, независимо от текущего уровня стека. В определенном смысле такое поведение системной переменной $ETRAP аналогично обычной локальной переменной, с тем исключением что эта переменная всегда существует и при старте процесса по умолчанию имеет значение пустая строка. Присваивание значения $ETRAP само по себе не выполняет ав­томатического ограничения области действия.
Дополнительно можно отметить, что зачастую после применения ко­манды NEW к переменной производится ее присваивание, и для со­кращения таких операций система MiniM поддерживает расширенную (нестандартную) инициализирующую форму команды NEW. В MiniM можно использовать оба варианта как равноценные:
new $etrap set $etrap=handler new $etrap=handler
Интересно, что, в отличие от локальных переменных, команда NEW в применении к системной переменной $ETRAP не изменяет ее значение, а создает копию на текущем уровне стека, и дальнейшие команды SET изменяют значение $ETRAP уже начиная с текущего уровня. Пример рутины для выполнения NEW применительно к переменной $ETRAP:
5.3. ETRAP 373
setnew ; k d setnew^ETRAP w
s $et=123 w "1: ",$et,! d subset w "4: ",$et,! q
subset
n $et w "2: ",$et,! s $et=456 w "3: ",$et,! q
При выполнении рутины получаем последовательность значений $ETRAP:
USER>k d setnew^ETRAP w 1: 123 2: 123 3: 456 4: 123
Здесь видно, что в точке 2 значение $ETRAP осталось неизменным, и, несмотря на изменение в точке 3, при возврате в точку 4 предыдущее значение было восстановлено.
При переходе к обработке ошибки процесс выполняет значение пере­менной $ETRAP вместо текущей выполняемой строки. Процесс выпол­няет ее на манер команды XECUTE, но без организации нового уровня стека.
После окончания выполнения строки команд, указанной в $ETRAP, и, выполнив возврат по стеку, процесс проверяет значение системной пе­ременной $ECODE, и, если оно пустое, то выполнение продолжается со следующей строки. Если не пустое, то процесс возвращается по стеку на предыдущий уровень и снова выполняет значение $ETRAP как после­довательность команд. На предыдущем уровне стека значение $ETRAP может уже быть другим.
После окончания выполнения строки команд выполняется неявно по­следовательность команд возврата по стеку в следующей форме:
quit:$quit "" quit
Это означает выполнить безаргументный возврат, если возврат по контексту не ожидается, и вернуть пустую строку, если возврат ожида­ется. При этом значение системной переменной $TEST такой логической проверкой не затрагивается и не изменяется.
374 ГЛАВА 5. ОБРАБОТКА ОШИБОК
Для задания обработчика ошибок необходимо, как минимум, выпол-
нить два действия:
1. Присвоить переменной $ETRAP строку с последовательностью ко­манд для обработки ошибки.
2. Эта последовательность команд должна явно или неявно изменить или не изменить значение переменной $ECODE.
Остальные системные переменные и системные функции служат коду обработчика ошибок индикатором состояния, по которому он принимает решение.
Простой вариант, показывающий возврат управления по стеку при обработке ошибок:
proc() ; k d proc^ETRAP() w
s $etrap="w $st,!" w 1/0 q
При его выполнении получаем сначала срабатывание обработчика на последнем, первом уровне, далее на предыдущем, нулевом:
USER>k d proc^ETRAP() w 1 0
При этом значение $ECODE не изменяется, и все время сохраняет свое значение:
USER>w $ec ,M9,
Рассмотрим вариант остановки обработки ошибки в коде обработчи­ка:
proc() ; k d proc^ETRAP() w
s $etrap="w ""stack="",$st,! s $ec=""""" w $$sub()+123,! d sub() w "last",! q
sub()
w "sub",! w 1/0 q
5.3. ETRAP 375
При выполнении такого кода ошибка возникает в функции sub. Если она вызывается в контексте возврата значения, то возвращается пустая строка, иначе выполняется просто возврат. Дальнейшее выполнение про­должается на предыдущем уровне стека:
USER>k d proc^ETRAP() w sub stack=2 123 sub stack=2 last

5.3.3 $ECODE

Присваивание системной переменной $ECODE пустой строки для си­стемы исполнения MUMPS процесса означает, что обработка ошибки закончена и дальнейшую раскрутку стека не нужно выполнять.
Присваивание системной переменной $ECODE непустой строки, на­оборот, приводит к генерации ошибки с записыванием этого значения в переменную $ECODE. Заменим код генерации ошибки на принудитель­ное взведение процесса в состояние ошибки таким присваиванием:
proc() ; k d proc^ETRAP() w
s $etrap="w ""stack="",$st,!,""$ec="",$ec,! s $ec=""""" w $$sub()+123,! d sub() w "last",! q
sub()
w "sub",! s $ec="trap in sub" q
При выполнении такого кода получаем последовательность работы:
USER>k d proc^ETRAP() w sub stack=2 $ec=trap in sub 123 sub stack=2 $ec=trap in sub last
376 ГЛАВА 5. ОБРАБОТКА ОШИБОК
Здесь происходит все то же самое, что и при ошибке деления на ноль, но мы явно задаем, какую ошибку надо генерировать. Формально гово­ря, мы можем присвоить переменной $ECODE произвольное значение. Но стандарт содержит явные рекомендации использовать соглашения по формированию такого значения:
1. Содержание $ECODE задается последовательностью кодов оши­бок, разделенных запятой.
2. При происхождении очередной ошибки, если значение $ECODE не пусто, код ошибки надо дописывать в конец этого списка.
3. Коды ошибок следует начинать с символа M для ошибок, зарезер­вированных и определенных стандартом, с символа Z для расши­ренных ошибок реализации MUMPS системы и с символа U для ошибок прикладной программы.
Список стандартных ошибок, начинающихся с символа M, приведен в приложении. В рассмотренном примере код ошибки "M9" соответствует ошибке деления на ноль.
Кроме того, некоторые MUMPS системы поддерживают, для совме­стимости с обработчиком ZTRAP, также команду ZTRAP для явной ге­нерации ошибки, и в этом случае в значение $ECODE должнен быть за­несен код ошибки, начинающийся с символа Z, поскольку такая команда не описана в стандарте, генерируемая ей ошибка не входит в описанные, и поддерживается в качестве расширенной команды MUMPS системы:
USER>s $et=""
USER>ztrap "custom"
<Zcustom>
USER>w $ec ,ZZTRAP, USER>w $ze <Zcustom>
Здесь стандарт определяет поведение переменной $ECODE, а значе­ние расширенных $Z переменных остается на усмотрение реализации и в разных MUMPS системах может как совпадать, так и незначительно отличаться.
5.3. ETRAP 377
Что важно отметить, в языке допускается присваивание переменной $ECODE, и содержание преимущественно трактуется как список разде­ленный запятыми, но присваивание системной переменной в левосторон­ней форме функцией $PIECE() в языке синтаксически не поддержива­ется.
USER>s $p($ec,",",2)=45
S $P($EC,",",2)=45 ^ <SYNTAX>
при выполнении в Cach´e и
USER>s $p($ec,",",2)=45
<SYNTAX> :SET: *s $p($ec,",",2)=45
при выполнении в MiniM.
Способ создания нового уровня стека для обработчика $ETRAP не важен, это может быть вызов метки командой DO, вызов функции с ожиданием возврата, команда XECUTE, или блочная форма команды DO:
proc() ; k d proc^ETRAP() w
s $etrap="w ""stack="",$st,!,""$ec="",$ec,! s $ec=""""" d . w 1/0 x "w 1/0" w "last",! q
При выполнении такого кода управление продолжается на уровне сте­ка на уровень выше, чем уровень произошедшей ошибки:
USER>k d proc^ETRAP() w stack=2 $ec=,M9, stack=2 $ec=,M9, last
Для того, чтобы продолжить выполнение программы на том же уровне стека, необходимо передать управление из обработчика командой GOTO, например:
378 ГЛАВА 5. ОБРАБОТКА ОШИБОК
proc() ; k d proc^ETRAP() w
s $etrap="w $st,! s $ec="""" goto continue" w 1/0
continue
w "last",! w $st,! q
В случае покидания управления из последовательности обработки ошибок, заданной в обработчике $ETRAP командой GOTO, неявный возврат управления на уровень стека выше не выполняется, при вы­полнении такого кода исполнение программы продолжается с заданной метки:
USER>k d proc^ETRAP() w 1 last 1
В частности, обработчик ошибок может скорректировать ситуацию с данными, приведшими к ошибке и продолжить выполнение программы, в том числе с места, вызвавшего ошибку:
proc() ; k d proc^ETRAP() w
s $etrap="goto err" k a
do
w a,! w 1/0 q
err
w "err trap at $st=",$st,", $ec=",$ec,! i ’$d(a) s $ec="" s a=123 goto do q:$q "" q
Выполнение такого кода показывает последовательность обработки обеих ошибок - сначала вызывается обработчик из-за неопределенности переменной a, значение a корректируется, затем выполнение продолжает­ся с использования переменной a, затем генерируется ошибка деления на ноль, и обработчик в этом случае не останавливает обработку очисткой переменной $ECODE, управление возвращается на уровень выше, снова срабатывает обработчик $ETRAP, но на верхнем уровне, вне контекста текущей рутины, конечно нет возможности определить метку err.
USER>k d proc^ETRAP() w err trap at $st=1, $ec=,M6,
5.3. ETRAP 379
123 err trap at $st=1, $ec=,M9,
goto err ^ <NOLINE>
Последнее сообщение в этом примере может отличаться для разных MUMPS систем. Одни могут запретить переход по GOTO по причине отсутствия текущей рутины, другие могут запретить выполнение GOTO на нулевом уровне стека, не проводя проверок на существование рутины и метки.
Также, как и последовательность команд, выполняемая в качестве аргумента командой XECUTE, всегда заканчивается неявной командой QUIT, даже если это не было указано, также и последовательность ко­манд, задаваемая обработчику ошибок $ETRAP, всегда заканчивается неявной последовательностью команд
quit:$quit "" quit
Важно отметить, что в случае, если обработчику ошибок $ETRAP был указан код, явно передающий управление на другую строку ко­мандой GOTO, уровень стека сохраняется и ответственность за стек и управление возвратом по стеку берет на себя разработчик. Если управле­ние было явно передано по стеку командой QUIT, то именно эта команда и будет использована для возрата по стеку и дальнейшие команды, ука­занные в $ETRAP, в том числе и неявный возврат, исполняться не будут, до них просто не дойдет управление.

5.3.4 $ESTACK

Парной к системной переменной $ETRAP является системная перемен­ная $ESTACK. Эта переменная используется в качестве маркера стека для обработки ошибки в форме, где это важно. Типовое назначение пе­ременной $ESTACK - маркировать уровень стека, чтобы выполнить рас­крутку стека до определенного уровня, отмеченного этим маркером, и только после этого выполнить обработчик ошибок.
Переменная $ESTACK была введена в стандарт языка в 1995-м го­ду, наряду с системной переменной $STACK и системной функцией $STACK(). Все три используются для программного анализа состояния стека.
Переменная $STACK при старте процесса всегда равна 0, при уве­личении уровня стека при вызове подпрограмм, блочной командой DO
380 ГЛАВА 5. ОБРАБОТКА ОШИБОК
или командой XECUTE, всегда увеличивается на 1 и при возврате по стеку командой QUIT либо неявной командой QUIT всегда принимает предыдущее значение, или возвращается к значению на 1 меньше. В определенном смысле переменная $STACK показывает абсолютное зна­чение уровня стека.
Переменная $ESTACK также при старте процесса равна 0, и при каждом увеличении уровня стека увеличивается на 1. Но при возвра­те по стеку принимает предыдущее значение. Отличие от переменной $STACK в том, что если к переменной $ESTACK применена команда NEW, то на этом уровне стека переменная $ESTACK обнуляется и ее рост продолжается снова на 1 на каждый уровень стека. Но при возврате по стеку значение $ESTACK восстанавливается. Таким образом, если на уровне N к переменной $ESTACK применить команду NEW, то на нем она станет равной 0, и при возврате по стеку станет равной N 1.
Пример такого поведения показывает программа:
estack ; k d estack^ETRAP w
w "1: ",$es,! d subes w "7: ",$es,! q
subes
w "2: ",$es,! n $es w "3: ",$es,! d subes2 w "6: ",$es,! q
subes2
w "4: ",$es,! n $es w "5: ",$es,! q
При выполнении получаем последовательность значений $ESTACK:
USER>k d estack^ETRAP w 1: 1 2: 2 3: 0 4: 1 5: 0 6: 0 7: 1
Переменная $ESTACK по своему поведению предназначена для от­носительной маркировки стека, и, в частности, для обработки ошибок,