Программы в Cache


Продолжается подписка на наши издания! Вы не забыли подписаться?

Программы в Cache

Типы программ.

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

Окончание Тип
программы
Пояснение
.MAC Макрокод Макрокод может содержать код Cache ObjectScript, макродирективы, макросы, встроенный SQL и встроенный HTML. Он компилируется в промежуточный код, а тот уже в объектный код.
.INC Включаемая макропрограмма Включаемые макропрограммы могут содержать любой код, разрешенный в .МАС-программах. Они применяются для построения макробиблиотек и могут быть включены в макропрограмму с помощью директивы Include -отсюда и их название.
.INT Промежуточный код Промежуточный код - это рабочий код Cache ObjectScript. Макросы и встроенный SQL сначала преобразуются в промежуточный код.
.OBJ Объектный код Прежде чем выполнить промежуточный код, необходимо откомпилировать его во внутренний объектный код. Это происходит автоматически при сохранении отредактированной программы. Во время выполнения используется только объектный код; Фирмы-разработчики программного обеспечения тиражируют свои приложения, большей частью, только в объектном коде.
Таблица 1. Различные типы программ.

Создание программ с помощью Cache Studio

Cache Studio — это одно из приложений, вызываемых из меню Cache-куба. Для разработки программ предлагается графический редактор, позволяющий комфортно создавать классы (включая создание кода методов), CSP-страницы Cache (Cache Server Pages), программы на макроязыке, включаемые файлы и программы на промежуточном коде, а также компилировать их в выполняемый объектный код.

Cache Studio предоставляет программисту такое вспомогательное средство, как автоматический синтаксический контроль; различные элементы языка помечаются разным цветом и в случае нарушения синтаксических правил выводятся сообщения об ошибках. Cache Studio также содержит встроенный отладчик, который может быть использован для поиска ошибок в приложениях.

Программисты часто используют Cache Studio совместно с проводником Cache, который предоставляет богатейшие возможности по управлению программами, включая их импорт/экспорт, а также контекстный поиск.

Основные структуры в программе

Программы в Cache состоят из индивидуальных блоков кода, создаваемых и редактируемых как исходный код, при сохранении автоматически компилируемых в особую объектную форму, не являющуюся машинным кодом и независимую от платформы — это так называемый р-код.

В качестве имени программы допустима любая комбинация буквенно-цифровых символов 7-битного набора ASCII, с тем лишь ограничением, что имя не должно начинаться с цифры, и с тем дополнением, что первым символом может быть знак процента, а также в любой позиции имени кроме первой и последней может использоваться символ точки. Программы, имена которых начинаются с процента, считаются библиотечными программами, эти имена предусмотрены для системных программ Cache, которые могут вызываться из любых областей Cache. В отношении максимальной длины имени каких-либо ограничений нет, однако распознаются лишь символы с первого по 31.

Строение программной строки

Строка является основной структурной единицей программы. Вообще говоря, в Cache различают четыре вида программных строк:

Строки с метками

Строки кода

Строки комментария

Имеются два различных метода комментирования:

С-совместимые комментарии

Write $Char(65 /* 65 соответствует букве "А" */)

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

Выражение или команда могут быть расположены в нескольких строках. Очевидно, действует правило, что команда не может быть разделена на командное слово и первый символ аргумента команды.

Set sum = x + у	// comment I
+ z  ,	// comment 2
mue = sum /3 	// comment 3

В этом примере комментарии расположены в конце каждой «подстроки».

В дополнение к этому надо учитывать, что строка всегда рассматривается как единое целое в строчных формах команд For, If и Else и поэтому не может быть разбита на несколько.

Максимальная длина строки программы 4К.

Блоки в программе

Следующую по размеру структурную единицу представляют собой блоки. В Cache различают три типа блоков:

  1. «Точечные» блоки, вызываемые при помощи команды Do без аргументов.
  2. Процедурные блоки, которые заключены в фигурные скобки и поэтому могут располагаться на нескольких строках.
  3. Командные конструкции условного выполнения программы, в которых исполняемый программный код также заключен в фигурные скобки.

«Точечные» блоки начинаются одной или несколькими точками в каждой строке, поэтому они так и называются. Вызывают их с помощью безаргументной команды Do.

Точечные блоки расширяют область действия программной строки. Инициализация такого блока происходит посредством команды Do без аргумента, при этом ожидается, что этот блок начинается в следующей строке после Do. Эта область должна начинаться с точки (после признака начала строки). При этом аналогично команде Do с аргументом Do-стек увеличивается на единицу (уровень 1). Если в этой области встречается еще одна безаргументная команда DO, следующая строка должна начинаться с двух точек (уровень 2) и так далее.

Отдельные блоки не обязаны, но могут завершаться командой Quit. Они заканчиваются неявно, если вслед за блоком с n-ным количеством точек идет строка, в начале которой содержится отличное от n число точек.

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

В следующем примере поясняются основные структуры блоков.

<code> // Эта (основная) область относится к уровню 0, 
// который не содержит точек 
Do  // безаргументная Do
. // Внешний блок, одна точка, уровень 1 
. <Code> 
. Do // еще безаргументная Do
.. // внутренний блок, две точки, уровень 2 
.. <code>
. // вновь блок первого уровня —
. // неявное завершение 2-го уровня блока 
. <code>
// Отсутствие точки, основной блок 
Quit

В заключение необходимо сделать еще два важных замечания. Во-первых, каждый уровень блока имеет собственную служебную переменную $Test, другими словами, изменение $Test не передается наружу. (Это существенное отличие от вызова подпрограммы с помощью команды Do с аргументом!). Во-вторых, как это происходит в нашей программе-примере, точки должны проставляться и у строк комментария с тем, чтобы блок не завершился автоматически.

Локальные переменные в программах

Общая структура программы

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

Р1 // первая строка программы
Set a=l
Do Р2
Quit
Р2 // первая строка подпрограммы Р2
Set х=2,у=3
Quit

Эта программа состоит из двух фрагментов, программы Р1 и вызываемой из Р1 программы Р2. Из совершенно другой программы можно было бы «глобально» вызвать Р1 с помощью команды Do ^P1. Это означает, что программа будет загружена из каталога программ в память и далее последует ее выполнение.

Если циркумфлекс («знак глобала») при вызове Do P2 отсутствует, то ожидается, что метку следует искать в текущей программе. Р2 можно назвать также подпрограммой Р1.

Вход в «глобально» вызываемую программу может совершаться и с определенной метки. К примеру, вызов Do P2^P1 вызывал бы (локальную) подпрограмму Р2 в программе Р1.

Наконец, имеется привлекательная — однако таящая в себе некоторую долю коварства — возможность, используя так называемый синтаксис метка+смещение, войти в n-ную строку (n равно смещению) после метки в вызываемой программе. Так, например, в нашей программе вызов Do Р1+3^Р1 означал бы, что вход в программу совершается через вторую строку после метки Р1, и там начинается обработка. В этом случае строка Set a=1 была бы пропущена. Эта возможность весьма гибка, и применяется в самых разных ситуациях (например, при тестировании программ), но все же требуется соблюдать достаточную осторожность, особенно тогда, когда в программу будут добавляться строки, и таким образом смещение будет изменяться.

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

Invert(xl,x2,x3) // Метка со списком формальных параметров

Метки могут быть public (общедоступными) и private (внутренними), что определяется соответствующим ключевым словом. Если его нет, то по умолчанию они public. Метки public могут быть вызваны из любой программы, private — только из текущей.

Move(x) Private /* метка private, может быть использована
                      только внутри этой программы */ 
Rotate(x) Public /* это метка public, может быть вызвана из
                      любой программы */ 
Invert(х)       /* аналогично */

Прежде чем закончить рассмотрение меток, давайте остановимся на неявной команде Quit перед меткой с параметрами.

Start     //
           Write "Hello World",!
           // Здесь неявная команда Quit 
MySub(a) //
             Write a,!
            // Здесь неявная команда Quit 
MyProc(){
             Write "This is my procedure"
             // Неявная команда Quit в конце процедуры
           }

Неявная команда Quit предотвращает «сквозное продолжение» программы в том случае, если опущена явная команда Quit и следующая строка начинается с метки с параметрами. Это обусловлено тем, что параметры могут быть не определены.

Область действия локальных переменных

Как обстоит дело с видимостью локальных переменных внутри программы? Здесь Cache придерживается подхода, при котором все однажды созданные переменные в разделе текущего процесса видны повсюду и доступ к ним ограничивается программистом, если он к этому стремится. При этом под разделом понимается область данных процесса в оперативной памяти Cache. Раздел, наряду с загруженными программами и определенными локальными переменными, содержит также специфичные для процесса данные.

В нашем примере назначаются три переменные. Назначенная в программе P1 переменная a видна также в программе Р2. Более того, все назначенные в программе Р2 переменные, а именно, а, х и у, после выполнения программы сохраняются.

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

В Cache существует ряд возможностей для «очистки» локальных переменных:

Команда New

Идея команды New заключается в том, что в программе задается список локальных переменных, существующих только в этой программе (и во всех вызываемых из нее подпрограммах) и удаляемых неявно при выходе из программы. Здесь необходимо добавить, что команда New для глобальных переменных бессмысленна (и вызывает сообщение об ошибке). Список аргументов команды состоит из неиндексированных локальных переменных. Присутствие имени переменной в списке предполагает сохранение всех ее существующих потомков. С помощью команды New а будет сохранена и переменная а(1), если она существует.

Ранее определенные переменные с теми же самыми именами упрятываются и затем восстанавливаются, когда действие команды New завершается. Это означает, что от конфликта имен имеется надежная защита.

P1 // первая строка программы
New a
Set a=1 Do P2
Quit
Р2 // первая строка подпрограммы Р2
New х,у
Set х=а,у=3
Quit

В этом примере команда New ограничивает область существования переменной а программой Р1, где использована данная команда, и программой Р2, поскольку та вызывается из P1. Переменные х и у существуют в программе Р2 и удаляются при выходе из Р2 (другими словами, передача их значений обратно в вызывающую программу невозможна).

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

Передача параметров

Передача параметров представляет собой безотказный инструмент передачи информации из вызывающей в вызываемую программу (и обратно). Она находит применение в следующих ситуациях:

В Cache предусмотрены различные формы передачи значений:

В обоих случаях максимальное количество параметров в списке составляет 254.

Передача по значению (call by value)

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

Set a=1,b=2,c=3
Do P1(a,b,c)     // фактическими параметрами являются а, Ь, с
(...)
P1(r,s,t)         // формальные параметры — это r, s, t,
Set Summe=r+s+t  // они содержат значения а, Ь, с
(...)
Quit

Разумеется, фактические параметры должны существовать. Это могут быть произвольные выражения, вычисляемые и передаваемые в виде значений:

Do ^Р2($Length(xl)*2, ^0rder(Nr))

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

Формальные параметры в соответствии с их очередностью ставятся в соответствие с фактическими параметрами, и значение присваивается данной локальной переменной. В примере r имеет значение 1, s значение 2 и t значение 3.

Существенным является то, что формальные параметры в начале вызываемой программы подчиняются неявной команде New. При завершении вызываемой программы точно так же выполняется неявная Kill, так что формальные параметры фактически существуют только лишь в вызываемой программе и не вступают в конфликт с одноименными переменными, которые уже были определены ранее. Это является предпосылкой для свободного от побочных эффектов программирования. И в заключение хотелось бы сделать два замечания. Отдельные фактические параметры могут отсутствовать, однако запятые должны быть поставлены. Соответствующие формальные параметры при этом не определены.

Do ^P1(,p1,,p2,$Horolog+l)

Первый и третий параметры отсутствуют. Если отсутствуют все параметры, скобки все же необходимы.

В списке формальных параметров не должны встречаться повторения:

P1 (x, t, х, у, z) // выдается сообщение об ошибке !!

Передача по ссылке (call by reference)

«Возврат значений» результатов из вызываемой программы можно осуществить через общую память переменных раздела, при этом сохраняется упомянутый ранее риск дублирования имен переменных. Более элегантной и надежной является форма передачи по ссылке.

Эта форма предполагает установление отношения между переменной в вызывающей и вызываемой программе. Любое присваивание и даже удаление командой Kill в вызываемой программе воздействует на соответствующую переменную вызывающей программы.

Синтаксический символ передачи переменной по ссылке — это точка перед именем переменной в списке параметров. Переменная var может, однако, в отличие от метода передачи по значению, не существовать!

Kill var     // var более не существует
Do P1(.var)  // вызов с передачей по ссылке 
(...)
Р1(х)        // формальный параметр х
Set  x=0,x(l)=l,x(2),x(3)=3 
(...)
Quit

Дополнительно, с помощью передачи по ссылке можно осуществлять связь целых массивов, иными словами, локальных индексированных переменных, с вызываемой программой и наоборот. После того как мы покинули Р1, в нашей программе существуют переменные var, var (1), var (2), var (3). Переменная var, таким образом, точное отображение х. Точно таким же способом осуществляется передача массива в вызываемую программу.

Разумеется, при вызове программы обе формы передачи параметров могут смешиваться.

Do ^Summe (a,b, с, . sum)

Процедуры

Структура процедуры

Выше обсуждалась концепция области видимости локальных переменных в Cache. Команда New в подпрограмме сохраняет локальные переменные, используемые в ней, и таким образом изолирует их от внешнего мира. Таким образом, разработчик должен создать список всех локальных переменных, которые необходимо сохранить. При этом легко пропустить необходимую переменную. С другой стороны, New без аргументов сохраняет все переменные, что приводит к дополнительным накладным расходам и поэтому должна использоваться осторожно.

Для упрощения управления локальными переменными, а также из общих соображений, Cache поддерживает процедуры. Процедура задается в следующей форме:

<procedurename> (<formal list>) [<public list>] <access> {
code ...
}

Например:

MyProc(X,Y) [A,B] Public {
Write "X + Y = ", X+Y,!
 Write "A + В = ", A+B,!
}

Эта процедура, названная МуРгос, объявлена как public, имеет два формальных параметра (х и y), и две public переменные (A и B). Основные синтаксические компоненты:

Ключевое слово <access> всегда либо public, либо private. Процедура, объявленная как private, может быть вызвана только из той программы, которая ее содержит. Процедура, объявленная как public, может быть вызвана из других программ. По умолчанию, если явно не указано, процедура объявляется как private.

Код процедуры

Следующий список обобщает правила и принципы, применяемые при создании кода процедуры.

МуРгос()        МуРгос ()  {
Label {        label

Если косвенность или команда Xecute ссылаются на переменную внутри процедуры, всегда предполагается, что эта переменная типа public. Это прямое следствие тех фактов, что эти две языковые конструкции выполняются вне процедуры, а внутренние переменные недоступны снаружи. Это правило распространяется и на использование меток в этих конструкциях. Следовательно, поскольку команда Goto может ссылаться только на метки внутри процедуры, команда Goto @A внутри процедуры не поддерживается.

И, наконец, не стоит забывать о функции $CASE, которая похожа на функцию $Select. В отличие от $Select, эта функция может быть использована для программирования выбора без использования косвенности. Таким образом, $CASE внутри процедуры играет роль $Select.

Переменные внутри процедуры

Локальные переменные в процедуре могут быть общедоступными или внутренними. Так, переменные, описанные в <public list>, являются общедоступными, а все остальные локальные переменные, которые упоминаются в процедуре, по умолчанию являются внутренними. Это справедливо, даже когда <public list> пуст. В этом случае его можно опустить при описании процедуры.

В дополнение к этому, внутренние переменные при входе в процедуру не определены и неявно удаляются при выходе из нее. Таким образом, они похожи на переменные, сохраненные командой New. Все %-переменные (то есть те, чьи имена начинаются со знака %) по умолчанию имеют тип public. Для нужд документирования программного кода они могут быть указаны в <public iist>, однако это не является обязательным.

Если внутри процедуры вызывается подпрограмма, внешняя по отношению к процедуре (расположенная вне тела процедуры), все внутренние переменные сохраняются и затем восстанавливаются при возврате в процедуру. Другими словами, вызываемая процедура или подпрограмма имеют доступ только к переменным с атрибутом public u к %-переменным (которые всегда public).

Однако переменные, сохраненные командой New, не эквивалентны внутренним переменным. Если какая-либо переменная (допустим, name), создаваемая в процедуре MyProg, должна быть доступна и в вызываемых ею внешних процедурах или подпрограммах, ее следует поместить в <public list>. Однако общедоступная переменная с таким же именем вполне могла существовать и в момент вызова MyProg. Поэтому разумно сохранить переменную name командой New. Это гарантирует, что они будут неявно удалены, когда произойдет выход из процедуры MyProg.

MyProg(x,y)    [name]   {
New name
Set name="John"
Do  ^Prog
Quit
}

Переменная name присутствует в <public list>, поэтому она видима вне процедуры, следовательно, и в вызываемой программе ^Рrog. Переменная name исчезает при выполнении команды Quit, которая завершает процедуру.

Команда New не допускает в качестве аргумента внутренние переменные. Действительно, команда New x вызовет сообщение об ошибке, если х не объявлена как public (или не начинается с символа «%»).

Приведенные правила относятся и к переменным, перечисленным в списке формальных параметров процедуры.

MyProc(x,y) [x] { 
Do Customer^Progl
}

В этом примере лишь переменная х (но не у!) будет видима в программе Customer^Prog1.

Внешние функции

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

Здесь напрашиваются рекомендации, когда же использовать пользовательскую функцию, а когда подпрограмму.

Структура и использование

Вызов пользовательской функции осуществляется путем задания ее имени, которое в отличие от «нормальной» встроенной функции предваряется двумя знаками доллара:

$$Name(список аргументов)

Соглашение о передаче параметров следует обыкновенным правилам для подпрограмм. При этом аргументы передаются либо по значению, либо по ссылке. Вызов функции без аргументов задается пустой парой скобок.

Как уже упоминалось, пользовательские функции и подпрограммы имеют сходство. Два их основных отличия:

Пользовательская функция устроена следующим образом:

Name(формальные параметры)
<code>
Quit <expr>

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

Первый несложный пример, возможно, пояснит строение тела функции и вызов пользовательской функции. Допустим, необходимо получить целое случайное число из диапазона 100—200. Встроенная функция $Random(n) помогает лишь косвенно, так как возвращает целое случайное число из диапазона [0,n—1]. Следующая пользовательская функция решает эту задачу для любого интервала [min.max]:

Random(min,max)
New diff,zz
Set diff=max-min, zz=$Random(diff)+1+min
Quit zz

Как видим, команда Quit завершает функцию, а значение аргумента команды возвращается в качестве значения функции. В примере можно было бы также избавиться от создания переменных zz и diff, написав сразу Quit $Random(diff) +1+min. Аналогично подпрограмме пользовательская функция может храниться локально или же «глобально» в другой программе. Если код функции находится в текущей программе локально с меткой Random (min, max), достаточно вызова:

Write $$Random(100,200)

Если же функция $$ Random не существует в локальной программе, а была сохранена как отдельная программа, то при вызове подпрограммы добавляется циркумфлекс:

Write $$^Random(100,200)

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

Write $$Random^Function(100,200)

Таким образом ссылаются на метку Random внутри программы Function.

В отличие от встроенных функций, вызов может дополнительно содержать параметры, передаваемые по ссылке. Мы расширим задачу только что рассмотренной функции $$Random: необходимо создать 10 целых случайных чисел из диапазона [min,max] (повторения допустимы) и поместить их в локальную индексированную переменную random (i), i=1...10. Программа имеет следующий вид:

Random(min,max, х)
New diff,i
Set diff=max-min,
For 1=1:1:10 Set x(i)=$Random(diff)+min
Quit ""

В цикле For создается локальный массив х (i) с 10 случайными числами. При вызове функции передаются три параметра, причем третий параметр передается по ссылке. Вызов этой пользовательской функции осуществляется, например, со следующими параметрами:

Write $$^Random(100,200,.random)

И это все! При формальном взгляде результатом вызова функции является пустая строка, так как выражение, заданное в аргументе команды Quit, — это пустая строка. Но был создан массив random (i), i=1...10 с 10 случайными числами из диапазона от 100 до 200, который в дальнейшем может быть использован вызывающей программой.

Процедурное структурированное программирование

Для управления последовательностью выполнения программы разработчик в Cache ObjectScript может выбирать между двумя методами:

Исключая специальные случаи, эти два метода не должны смешиваться. Хотя — до некоторой степени — они используют одинаковые ключевые слова (If, Eise и For), соответствующие команды синтаксически и семантически отличаются. Для новых разработок настоятельно рекомендуется использовать только новые условные конструкции и циклы.

Ниже в общем виде описаны четыре конструкции:

If <expr>[,<expr> . . . ]  {code}
ElseIf <expr>[,<expr> . . . ]  {code}
Else {code}
For <forparameter> {code}
While <expr>[,<expr> . . . ]  {code}
Do {code} While <expr>[,<expr> . . . ]

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

Конструкции управления выполнением программы

Список выражений

Структуры этих четырех командных конструкций имеют несколько общих свойств. Все конструкции имеют блок {code}, который подробно описан ниже. В дополнение к этому, три конструкции If, While и Do/While включают в себя список выражений <ехрг> [, <ехрг> . . . ], который состоит из одного или более выражения, разделенных запятой и вычисляемых слева направо. Примерами таких выражений могут быть:

(age>30)&&(sex="W") 
$Piece(^G(l,typeno),"/",4)'?3N 
r<5,s>2,t<10

Обработка таких списков подразумевает проверку каждого выражения на истинность. Обработка списка прекращается, как только встречается выражение, которое дает FALSE (эквивалентно 0).

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

Условные конструкции: If/Elself/Else

В самом общем виде условная конструкция имеет следующий вид:

If <expr>[,<expr> . . . ] {code} 
Elself <expr> [,<expr> . . . ] {code} 
Else {code}

Заметим, что ключевые слова Elself и Else необязательны. Применяются и такие конструкции:

If <expr>[,<expr> ...] {code}
If <expr>[,<expr> ...] {code}
Else {code}
If <expr>[,<expr> ...] {code}
Elself <expr>[,<expr>...] {code}

Секция ElseIf может встречаться многократно, как видно из следующего примера:

If b=5 {set a=1} ElseIf b=6 {Set а=2} ElseIf b=7 {Set  a=3} 
Else  {Set a=4} Goto x

Как это принято в условных выражениях такого типа, выполняется не более одного блока кода, а именно:

Пример, приведенный выше, достаточно прост для понимания. Однако необходимо подчеркнуть, что заключительная команда Goto выполняется всегда, поскольку она находится вне конструкции If. Для улучшения читаемости данный пример может быть переписан следующим образом:

If b=5 { 
Set a=1 .
} ElseIf b=6 {
Set a=2 
} ElseIf b7 {
Set a=3 
} Else { 
Set a=4
}
Goto x

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

If x<3,z>5 {
Set a=l 
Do Label1 
Set b=2 
Do Label2 
} Else { 
Do Label9

Команда Quit внутри блока кода выполняется так, как будто его нет. В зависимости от контекста, в котором вызывается команда Quit, она завершает либо программу, либо цикл For.

Команда Goto может передавать управление либо вне конструкции If, либо на метку внутри нее, но никогда в другой блок кода, вне зависимости от того, находится он внутри текущего блока или нет.

Наконец, надо заметить, что ElseIf и Else нe являются независимыми командами, это части командной конструкции If. Таким образом, ElseIf и Else не могут быть использованы без предшествующей If. Следует отметить, что при выполнении командной конструкции If системная переменная $Test не устанавливается и не используется.

Циклы: For

Командные конструкции For, While и Do/While обеспечивают многократное выполнение заданного сегмента кода. Это называется циклом. Общая форма команды For:

For <for parameter> {code}

Здесь блок { code } выполняется столько раз, сколько задано в параметре команды <for parameterx Команды, следующие в той же строке программы после закрывающей скобки «}», уже не относятся к команде For и выполняются только один раз.

Интересной особенностью команды For в Cache является разнообразие вариантов параметра команды. Здесь возможны:

For lv=<expr> [,<expr> . . . ] {code}

например

For prime=2, 3, 5, 7, 11 {Write !, prime," это простое число"} 
For vowel="a","e","i","o","u" { 
  Write ! , vowel, " это гласная"
}
For lv=<num expr>:<num expr>:<num expr>   {code}

где первое выражение рассматривается как начальное значение, второе, как величина инкремента и третье как конечное значение переменной цикла. Например

For i=1:1:10 {Write !,"i," ",i**2}
For delta=-2: .1: 0 {Do "function (delta) }
For  lv=<num expr>:<num expr>   {code}

например:

Set pa=1 
For 1=3:2 {
  Do $$^PrimeTest(i, .ptest) 
  If ptest=1 {
    Write !,p," простое" Set pa=pa+l 
    If pa>100 { 
      Quit
    }
  }
}
For     {code}     //  Как минимум два пробела после  For   !

например:

For   {
  Write  $Char(7)
  Hang  1
  Quit:$Piece($Horolog,",",2)>72000) 
}

Команда Quit внутри цикла завершает его, как показано в двух последний примерах. Во многих случаях постусловие задает критерий окончания цикла. Команда Goto, передающая управление на строку вне цикла также завершает его.

Следующие основные правила описывают применение числовой формы аргумента команды For с переменной цикла:

Необходимо отметить, что различные формы конструкции For могут свободно комбинироваться.

For lv=2,5,49,1:1:15,1:2 (...)  //Смешанный формат
For 1=1:1:3 { 
  For j=l:1:5 { 
    Set m(i,j)=0 
    If i=j {
      Set m(i,j)=1 
    }
  }
}

Первый цикл состоит из трех форматов, списка (lv=2,5,49), числового диапазона (lv=1,2,... ,15) и числового диапазона без конечного значения, что подразумевает завершение этой части цикла принудительно.

Следующий пример демонстрирует вложенные циклы For. При этом создается матрица (3*5), у которой все элементы (кроме диагональных) равны 0, а элементы диагонали — единице.

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

Циклы: While и Do/While

Две управляющие конструкции

While <expr>[,<expr> . . . ] {code}
Do {code} While <expr>[,<expr> . . . ]

также являются циклами. Они отличаются методом проверки условия окончания цикла. В конструкции While условие проверяется до выполнения блока программы, а в Do/While — после. Кроме того, чтобы блок программы выполнился, все выражения в списке должны быть логически истинными.

While x>3,y>4 {
 Set a=5 
Do Label 
}
Write "Готово!" 
Do {
Set a=5 Do Label1 
} While y>4,y<10 
Write "Готово!"

Важное отличие между этими конструкциями заключается в том, что в случае Do/While блок кода выполняется хотя бы один раз, чего нельзя сказать о While. Команда Write в обоих примерах не входит в циклы.

Как было показано в примерах для команды For, команда Quit внутри блока кода прерывает цикл.

Команда Goto может передавать управление в любую точку вне блока кода или на метку внутри данного блока на том же уровне, но не в другой блок кода, независимо от его местоположения (внутри или вне текущего блока).

Команда CONTINUE

Команда CONTINUE может использоваться в пределах блока кода, следующего за командами For, WHILE, или Do WHILE. Это приводит к переходу обратно на начало цикла для проверки необходимости повторного выполнения этого блока кода. Таким образом, команда CONTINUE имеет тот же эффект, что и достижение закрывающей скобки блока кода. Команда CONTINUE может быть использована с постусловием. Как и после любой безаргументной команды, после нее должно быть минимум два пробела.

В качестве примера возьмем фрагмент кода из раздела о циклах For, который распечатывает первые сто простых чисел.

Set pa=1
For p=3:2 {
  CONTINUE : ' $$"PrimeTest (p) 
  Write !,p," простое" Set pa=pa+1
  If pa>100 { 
    Quit
  }
}

Если функция $$^PrimeTest(р) возвращает 0, текущее выполнение тела цикла прерывается. Если получена 1, значит, было найдено простое число, постусловие в команде не будет выполнено и команда CONTINUE будет пропущена.

Основные правила для блоков {кода}

Правила

Следующие правила относятся к блокам кода внутри управляющих конструкций:

  1. внутри того же блока;
  2. в блок кода, внутрь которого вложен текущий блок кода;
  3. на строку вне всех блоков кода.

Goto не может передать управление в новый блок кода.

Примеры применения блоков кода

Следующие несколько примеров помогут понять основные принципы.

Классическая условная конструкция If /Else в одной строке:

If x>3,y<4 {Set z=1 Do label1) Else (Set z=2 Do label2}

Функционально эквивалентен первому, но размещен в нескольких строках программы:

If x>3,y<4 {
Set z=1 
Do label1 
} Else { 
Set z=2 
Do label2
}

Вложенные управляющие конструкции, в которых выражение Do tag является частью цикла For и выполняется при каждой итерации:

For y=1:1:10   {
If abc(y)>0   {
Set x=x+abc(y)
}   Else   {
Set x=x-abc(y)
}
Do tag
}

Условные конструкции, ориентированные на строки

Если сразу за аргументом команд If , Else или For не следует блок кода, то Cache считает их классическими командами, ориентированными на строки, когда область действия их аргумента распространяется только до конца строки, в которой они находятся. Из соображений эффективности управления стеком и лучшей читаемости программного текста рекомендуется не смешивать эти две формы (классическую форму, ориентированную на строчную обработку, и блочные конструкции). Однако поскольку могут применяться оба варианта, ориентированные на строки управляющие конструкции требуют некоторых пояснений.

Если строчно-ориентированная команда If, Else или For применена внутри блока кода и блок заканчивается раньше, чем заканчивается строка (то есть встречается закрывающая фигурная скобка), здесь же заканчивается и действие этих команд.

If х<1  {Read "Name:   ",name:10 Else   Write "Timeout"}  Set x=1

Область действия строчно-ориентированной команды Else в блоке кода конструкции If составляет этот блок кода. Таким образом, команда Set x=1 выполняется всегда.

Правила, по которым одна строка программы может быть разбита на несколько, не применимы к строкам, содержащим команды If, Else и For в их строчно-ориентированной форме. Таким образом, конец строки всегда соответствует концу области действия этих команд. Конструкции, начинающиеся в зоне действия строчно-ориентированных команд, должны заканчиваться в той же строке.

If x<1,y<1 Set a=l If z {Set b=2 
Set c=3} Else (Set k=5}

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

If x<1,y<1 Set a=1 If z {Set b=2 Set c=3}
Else {Set k=5}

также вызовут ошибку, поскольку здесь команда Else не является самостоятельной. Она относится к блочной команде If, которая начинается на первой строке. Следовательно, команда Else должна находиться на той же строке, что и команда If, к которой она относится.

Следующие два примера показывают, как блочная команда For и команда For, ориентированная на строки, приводят к одинаковому результату, несмотря на различное их строение:

If x>3 { 
Set a=""
For {
  Set a=$Order(abc(a)) Quit:a=""  Set x=x+abc(a) 
} 
Set y=y+x
}
If x>3 {
  Set a="" For Set a=$0rder (abc (a)) Quit:a=""  Set x=x+abc(a) 
  Set y=y+x
}

$CASE в условных конструкциях

В условных конструкциях Cache могут быть использованы различные комбинации команд If/ElseIf/Else для достижения различных результатов.

Широко распространенным случаем является вызов той или иной программы в зависимости от значения переменной:

If a=1 {
Do ^Р1(3) 
} ElseIf a=2 {
Do ^Р2(2) 
} ElseIf a=3 {
Do ^РЗ(1) 
} Else {
Do Error }

Функция $CASE является вариантом функции $SELECT, позволяющей наиболее эффективно задавать различные альтернативы выполнения программы. Общая форма функции $CASE такова:

$CASE(<expr>,<lit>:<value>[,<lit>:<value> . . . ][,:<default>])

Эта форма функции SCASE имеет три категории аргументов. Первый аргумент <ехрr> рассматривается как выражение, например 2*х+1. Затем следует список аргументов вида <lit>:<value>. Каждый элемент списка состоит из двух частей, где <lit> является числовой или строковой константой, которая уже определена в момент компиляции, a <value> представляет собой значение функции $CASE (когда <lit> соответствует значению, полученному при вычислении начального выражения <ехрr>)

Если аргумент <default> не задан и ни одна константа <lit> не соответствует значению выражения <ехрг>, порождается сообщение об ошибке. Отсюда следует, что функция $CASE (также как и функция $select) всегда должна содержать значение по умолчанию.

Set x=1
Set y=$CASE(2*x+1,1:"A",2:"B",3:"C",:"Z")

В этом примере результат вычисления выражения 2*х+1 составляет 3. Функция $case находит третий вариант выбора и возвращает его значение («С»), которое присваивается переменной у. Если вычисление выражения не приведет к выбору ни одного из вариантов выбора, результатом функции $CASE будет значение по умолчанию «Z»

Интересно, что аргумент <value> функции $CASE может интерпретироваться иначе, чем это описано в определении функции, но только в сочетании с командами do и Goto. Если функция $CASE используется как аргумент команды Do или Goto, значение <value> интерпретируется как ссылка на точку входа. В этом случае <value> должно представлять собой допустимую метку, или допустимое имя программы (или вместе метку и имя, и может быть даже с параметрами). Таким образом, это не может быть выражение, которое требует вычисления.

Следующий пример показывает такой вариант $case:

Do $CASE(a,l:^Pl.(3) , 2 : ^Р2 (2) , 3 : ^РЗ (1) , : Error)

В зависимости от текущего значения переменной а, вызываются либо программы ^Р1, ^Р2, ^РЗ, либо программа Error. Аналогично выглядит и строка кода с командой Goto.

Эквивалентно можно было бы в данном примере использовать функцию $Select, однако это потребовало бы применения оператора косвенности. Поскольку применение косвенность в процедурах требует дополнительных усилий (см. 5.2.6.2), в процедурах предпочтительнее использовать функцию $CASE.

Обработка ошибок

Введение

Понятию обработки ошибок в английском языке соответствует термин Error Processing. Столь же употребительны понятия Error handling (обработка ошибок), Error trapping (перехват ошибок) и Exception handling (обработка исключений).

Вообще говоря, всеми этими терминами обозначаются меры, принимаемые в программе, если возникает какая-либо ошибка, которая в противном случае привела бы к прерыванию процесса выполнения. Известный, и не только в Cache, пример — это попытка деления на 0. Каждый программист сталкивается с ошибками, даже «правильные» программы при определенных условиях могут аварийно завершаться.

Первая мысль, возникающая у программиста, столкнувшегося с ошибкой, — это перейти в так называемый непосредственный режим. Здесь можно проанализировать обстоятельства, которые привели к ошибке, и найти контрмеры.

В Cache такая методика принципиально поддерживается благодаря наличию команды Break, позволяющей перейти в режим программиста или отладчика. Команда Break может быть снабжена различными аргументами, вызывающими разнообразные действия.

Для программных систем, установленных у клиента, перехода в режим программиста, конечно же, недопустим. Более того, в таких системах желательно сохранять возможно больше информации о контексте, в котором возникла ошибка, и затем продолжать выполнение программы с соответствующей точки, заданной входной ссылкой. Cache идет в этом отношении дальше, поскольку программисту дается гибкая возможность написать какой угодно код для выполнения в случае ошибки. Как правило, вызывается полноценная программа обработки ошибок. Эта программа может сохранить информацию об ошибке, включая ее местоположение и, дополнительно, информацию о текущем состоянии процесса, а также значения отдельных переменных.

Обзор обработки ошибок

Важными для понимания обработки ошибок являются следующие соображения:

<UNDEFINED>Labl+3^Programm.

Представленная таблица дает общий обзор различных элементов языка, предусмотренных для обработки ошибок.

Элемент языка Пояснения
Системные переменные $EC[ode] Она или пуста (""), или ей присвоен стандартизованный код ошибки. $ECode может быть присвоено значение для инициализации ошибки и проверки логики обработки ошибок.
   $ЕТ[rар] Содержит (любой) код, выполняемый после возникновения ошибки.
   $ZT[rap] Содержит указатель строки, на который в случае ошибки переходит управление и обработка.
   $ST[ack] Содержит число уровней стека.
   $ES[tack] То же, что и $Stack, но применима команда New.
   $Q[uit] Содержит значение ИСТИНА (1), если вызвана в контексте пользовательской функции, в противном случае в нормальной подпрограмме равна 0.
   $ZE[rror] Код ошибки Cache после возникновения ошибки, например,<SYNTAX>Label+2 Л Progl .
Функции $ST[ack] Состояние программной среды после возникновения ошибки на различных уровнях стека.
Команды ZQ[uit] Удаляет все или заданное число уровней стека.
   ZT[rap] Может быть задана в программе для того, чтобы смоделировать ошибку.

Таблица 2. Элементы языка для Error Processing.

Если возникает ошибка, Cache всегда запускает четырехступенчатый процесс:

  1. Если обработка ошибок была определена с помощью $ZTrap, то сначала программный стек возвращается в точку установки $ZTrap. Cache начинает выполнение программы со строки программы, заданной в $ZTrap.
  2. Если обработка ошибок задана посредством $ETrap, Cache выполняет команды, содержащиеся в $ЕТrар.
  3. Если обработка ошибок не была задана вообще и программа была запущена из режима программирования, на экране появляется соответствующее сообщение об ошибке. Стек программы остается неизменным, и программа остается в режиме программирования. Позже программист может вновь продолжить выполнение программы.
  4. Если обработка ошибок не установлена, в режиме приложения выдается сообщение об ошибке и выполняется команда Halt.

Перехват ошибок с помощью $Etrap

Как уже говорилось, обработчик ошибок может базироваться как на специальной переменной $ZTrap, так и на $ЕТrар. Для начала поясним механизм обработчика ошибок на основе $ЕТrар.

$ЕТrар – это системная переменная, устанавливаемая в программе командой Set. Она содержит код Cache, который выполняется, если возникает ошибка. Например, если в случае ошибки должна вызываться программа обработки ошибок ErrorHandlerl, TO выполняют Set $ETrap="Goto ErrorHandlerl", другими словами, это полная команда (а не только указатель строки, как это было для $ZTrap).

Для последующего изложения существенно понятие уровня стека. Всякий раз, когда выполняются команды Do, Xecute или пользовательская функция, уровень стека повышается на единицу и соответственно понижается, когда явная или неявная команда Quit заканчивает выполнение на этом уровне. В непосредственном режиме перед выполнением первой команды Do, Xecute или $$-функции уровень стека равен 0. Это значение хранится в специальной переменной $Stack и свидетельствует о достигнутой глубине вложенности.

Чтобы иметь возможность вызывать на разных уровнях различные программы обработки ошибок, $ЕТгар может быть указана в качестве аргумента команды New, а затем переопределена. Если встречается ошибка, специальная переменная $ECode содержит соответствующее сообщение об ошибке. Если $ЕТгар задана, стек программы возвращается на уровень, где осуществлялось присваивание $ЕТгар. Наконец, выполняется команда, заданная как значение переменной $ЕТгар.

Первый пример демонстрирует основы обработки ошибок:

P1  // (...)
Set $ETrap="Goto ErrorHandlerl" 
(...)
Write !,$$F1($Horolog) 
Quit
//_____________________________
Fl(h) // (...)
New $ETrap Set $ETrap="Goto ErrorHandler2" 
(...) 
Quit h
//_____________________________
ErrorHandler1 (...) Set $ECode="" Quit
ErrorHandler2 (...) Set $ECode="" Quit

В первой строке этой программы переменной $ЕТrар присваивается код, который должен выполняться в случае ошибки. В примере это переход к метке ErrorHandler1. В пользовательской функции F1() старое значение $ЕТrар сохраняется командой New, и присваивается новое значение с помощью команды «Goto ErrorHandler2». Следовательно, если в функции возникнет ошибка, то произойдет переход в другую программу обработки ошибок.

В только что рассмотренном примере $STack после входа в программу Р1 имеет значение 1, а после вызова пользовательской функции $$F1 () — значение 2.

Кстати сказать, обработка ошибок происходит на том же уровне, где возникла ошибка. Таким образом, уровень стека не повышается, а значит, удается избежать проблемы обработки ошибки, связанной с достижением максимально возможного уровня стека.

В обоих обработчиках ошибок, определенных в нашей программе Р1, перед заключительной командой Quit удаляют соответствующее условие ошибки, присваивая специальной переменной $ECode пустую строку. Если этим пренебречь, то условие возникновения ошибки далее оставалось бы постоянным.

У $Stack есть родственная системная переменная $EStack, которая также отражает текущий уровень стека. Обычно значения обеих переменных равны, однако в отличие от $STack, $EStack может появляться среди аргументов команды New, что позволяет сохранить текущее значение $EStack и снова начать отсчет с нуля.

Когда во время работы программы всплывает какая-нибудь ошибка, выполняется содержимое $ЕТrар, после чего происходит выход с текущего уровня стека. Эти действия осуществляются путем неявного включения в процесс выполнения следующих команд:

Xecute $ETrap Quit:$Quit "" Quit

При этом уровень выполнения закончится первой командой Quit, возвращающей пустую строку, если ошибка произошла в пользовательской функции, а значит, $Quit содержит значение ИСТИНА. Если это не так, то обычный вызов программы будет завершен безаргументной командой Quit. Итак, уровень стека понизился, и, если снова возникнет ошибка, дальнейшее целиком и полностью зависит от значения специальной переменной $ECode на данном уровне, если это значение здесь вообще существует.

Моделирование ошибки

Путем задания специальной переменной $ECode или же с помощью команды Ztrap можно намеренно вызвать ошибку, чтобы проверить логику обработчика ошибок.

Всегда, когда специальной переменной $ECode присваивается непустая строка, возникает ошибка. При этом переменной $ZError автоматически присваивается значение <ECODETRAP>, и управление передается обработчику ошибок, как это случилось бы в случае возникновения нормальной ошибки.

Иногда имеет смысл создать свои собственные коды ошибок. Как уже говорилось, содержащиеся в $ECode сообщения об ошибках обрамляются запятыми и начинаются с буквы Z, если речь идет об ошибках, специфических для Cache, и с буквы U, если это ошибки, специфические для приложения. Сообщение об ошибке, связанной с истечением пароля, может быть присвоено $ECode следующим образом:

Set $ECode=",UPassword expired,"

Ошибки могут быть вызваны также при помощи команды ZTrap (сокращенно zt). Как только ZTrap выполняется в программе Cache, аргумент команды помещается в специальную переменную $ZError. Если ZTrap применяется без аргумента, $ZError присваивается значение <ztrap>. $ECode, в свою очередь, в обоих случаях получает значение , ZTRAP, . С выполнением команды управление передается обработчику ошибок, как будто бы наступила нормальная ошибка.

Сохранение контекста ошибки с помощью $STack ()

Сохранить контекст выполнения программы после ошибки можно с помощью функции $STack() (сокращенно $ST). $STack() в различных формах дает информацию о действиях, которые привели программу к ошибке, либо, когда обработка ошибок неактивна (то есть $ECode=""), каким образом программа пришла к заключительной команде. $STack() определена в одноаргументной и в двухаргументной формах.

Первый аргумент всегда интерпретируется как целое значение, большее или равное -1. Второй аргумент может содержать лишь один из следующих кодов: «PLACE», «MCODE» или «ECODE», записанный прописными или строчными буквами, здесь это не имеет значения. Одноаргументная форма представлена в таблице23.

Вызов Возвращаемое значение
$Stack(-1) Максимальное значение уровня стека, для которого имеет смысл вызов двухаргументной формы функции.
$Stack(0) Зависимое от производителя значение, которое показывает, как стартовал данный процесс Cache.
$Stack (m) Если m из области 0 < m' > $STack(-l) , то возвращается одно из трех следующих значений: 1 . "Do", если вызов этого уровня произошел посредством команды Do. 2. "XECUTE", если вызов этого уровня был выполнен по команде Xecute. 3. "$$", если вызов этого уровня был осуществлен через пользовательскую функцию.
$Stack(n) Если n > $STack(-l) , то возвращается пустая строка.
Таблица 3. Результаты вызова одноаргументной формы $STack (intexpr).

Используя $STack(), можно проверить любой уровень стека, который достигался в процессе выполнения программы. Наибольшая глубина вложенности сохранена в $STACK(-1). В случае, когда обработка ошибок неактивна (то есть $ЕС=""), $ST(-1) всегда равна $ST. Если же обработка ошибок активизирована, то может случиться, что $ST(-1)>$ST, так как могли быть сохранены ошибочные ситуации, произошедшие на более высоком уровне стека, чем текущий.

С помощью $ST (m) (для m в пределах пройденного стека) можно определить, посредством какого вызова был достигнут этот уровень стека: через вызов подпрограммы {"DO"), через Xecute ("XECUTE") или же через вызов пользовательской функции ("$$").

В двухаргументной форме второй аргумент обозначает так называемый код стека, который должен представлять собой один из следующих кодов: "PLACE", "MCODE" или же "ECODE" . Все три кода относятся к уровню стека, заданному первым аргументом. Не вдаваясь в подробности, код «PLACE» дает справку о местонахождении ошибки, "MCODE" — о находящемся там Cache-коде, и "ECODE" — о характере ошибки.

Проще всего объяснить назначение "ECODE" . $STACK (2, "ECODE") возвращает код ошибки (если таковой вообще имеется) на втором уровне стека, например, ",М9,".

Код «PLACE» позволяет узнать место в строке программы внутри заданного первым аргументом уровня стека, причем оно может иметь, например, такой вид:

L1+3^|"Warehouse1"|MainProg +5

Здесь местоположение в программе задано обычным синтаксисом «метка + смещение» (L1+3) с последующим именем программы (HauptProg) и с информацией об области (Lager1) : ^|"Warehouse1"|MainProg). Смещение +5 указывает на команду в строке, повлекшую за собой ошибку. Причем, например, команда Set х=1,у=2 расценивается как две команды.

Несколько иной формат используется, если ошибка возникла внутри строки Xecute. Тогда задается не ошибочная строка, как это было ранее, а символ косвенности «@» плюс указатель на команду в данной строке.

О какой именно команде выдана информация, зависит от значения $ECode на данном уровне и наглядно показано в таблице 4.

$STack (m, "ECODE") $STack (m, "PLACE")
Пустая строка последняя выполненная команда, если m'=$STack
Пустая строка текущая выполненная команда, если m=$STack
Непустая строка последняя команда на данном уровне стека, для которой $STack (m, "ECODE") еще вернула бы пустую строку
Таблица 4. Значения $STack(m, "place") в зависимости от значения $STack(m, "ecode").

Эти результаты можно проинтерпретировать следующим образом. Оба первых случая показывают, что на заданном уровне стека (m) не произошла ошибка. Если m не равно текущей глубине стека, то отображается последняя выполненная команда. Если же, напротив, проверяемый уровень стека равен текущему, то показывается последняя выполненная команда на текущем уровне.

В случае, когда на заданном уровне имела место ошибка (иными словами, $ECode '='"), возвращается указатель на последнюю безошибочную команду.

За дальнейшими подробностями об обработке ошибок рекомендуем обратиться к документации по Cache, где в деталях описываются также и экзотические ситуации (например, появление ошибки в обработчике ошибок).


Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.

Copyright © 1994-2016 ООО "К-Пресс"