Первое знакомство с ARexx'ом
Источник: amitrans
Встроенный в версии ОС 2 и 3 язык программирования ARexx заслуживает отдельной толстой книжки. Не претендуя на исчерпывающее его описание, попробуем рассказать об основных моментах использования языка в Амигах.
Для начала представим себе такую ситуацию. У нас есть анимационный файл, в котором, ну, скажем 100 кадров. Мы хотим изменить формат каждого из кадров. Есть программа AdPro, которая позволяет делать с любым из кадров все, что угодно. Так что теперь, повторять 100 раз подряд одну и ту же операцию, каждый раз загружая и записывая очередной кадр? А если мы к тому же хотим плавно изменять палитру, постепенно затемняя кадры?
Или, допустим, у нас одновременно работают база данных и текстовый редактор. Неужели же для того чтобы "перегнать" некоторый текст из редактора в базу, надо сначала записывать файл, находясь в редакторе, потом переходить в базу, а оттуда загружать его?
Те, кто работал на IBM PC, зачастую как "Отче наш" знают текстовый редактор MultiEdit. Этот редактор имеет свой собственный макроязык, который позволяет легко автоматизировать и конфигурировать редактор по своему усмотрению. Разумеется, PC - не Амига; другой РС-шный редактор уже будет иметь СВОЙ макроязык, третий - свой и т.д. Да и попытку одновременного запуска MultiEdit вместе с CorelDraw и Excel, к тому же с возможностью параллельного и независимого обмена данными и командами между ними при помощи единого универсального макроязыка, оставляем в качестве домашнего задания для пытливого читателя.
Но вернемся к Амиге. Представьте ситуацию, когда вся многозадачная компьютерная система имеет мощнейший универсальный макроязык, интерпретатор которого работает на "заднем плане" абсолютно независимо от других происходящих в системе процессов... Этот интерпретатор определяет, имеют ли встречаемые им в командном файле конструкции отношение к нему лично. Если да - обрабатывает их сам, если нет - посылает их своему текущему "хозяину", который сам должен разобраться, что за команду ему подсунули. Хозяина можно менять в любой момент - им может быть не только любая из работающих прикладных программ, имеющих ARexx-интерфейс, но и вся операционная система Амиги.
Этот макроязык называется ARexx. "Стартером" для его интерпретатора является небольшая программа каталога System, которая называется Rexxmast. Существуют также компиляторы ARexx-программ, специально предназначенные для тех программистов, которые "попав" в ARexx, "выскочить" из него уже не могут. По мощности этот язык ни в чем не уступает скажем, такому гиганту, как С (разве что по скорости исполнения - скомпилированные ARexx-программы все-таки работают медленнее С-шных). Пусть вас не удивляет размер программы Rexxmast - чуть больше двух килобайт. Это просто сервер процесса, открывающий порт ARexx-a. Вся сила спрятана в ARexx-овских библиотеках, открываемых только тогда, когда нужно (никаких там сотен килобайт памяти ARexx от системы не отъедает, невзирая на свои просто абсолютные возможности; в среднем расходуется около 40 Кб для работы интерпретатора). Rexxmast лучше всего запускать из файла S:User-startup, вставив туда строчку:
System/Rexxmast >NIL:
Можно также перенести иконку программы RexxMast из каталога System в каталог WBStartup, после чего запуск Rexxmast также будет происходить автоматически. Однако в этом случае каждый раз после старта системы будет вылетать окошко, напоминающее об авторских правах на ARexx.
После своего запуска сервер ARexx-a открывает связной порт и "ложится спать", не отнимая далее у системы драгоценного процессорного времени. Разбудить сервер может любая попытка послать сообщение в открытый им порт, после чего будут предприняты немедленные действия, определяемые характером этого сообщения. Следует заметить, что связные порты не являются прерогативой ARexx-a - на них построена вся ОС Амиги, ARexx в данном случае "всего лишь" пользуется возможностями ОС.
Все программы, использующие ARexx, должны включать в себя некоторые средства для связи с его интерпретатором. Для этого требуются крайне незначительные добавки к программам, встроить которые не представляет особого труда. Предельного же уменьшения размеров дополнительного кода можно добиться, использовав разделяемые библиотеки (например, easyrexx.library), которыми одновременно может пользоваться любое число программ для реализации требуемых ARexx-возможностей.
Язык Rexx разработал Майк Кулешов для mainframe-компьютеров фирмы IBM в начале 80-х годов. Автором амиговской версии является Вильям Хейз (первая буква "А" названия амиговской версии языка означает "Amiga"). ARexx встраивается в систему, начиная со второй версии ОС. Для более старых ОС - он может быть установлен "вручную" - первые версии ARexx появились еще в 1987 году.
Итак, подытожим, что такое ARexx:
- Универсальный язык программирования.
- Средство межпрограммного общения.
- Универсальный системный макроязык.
Число программ, так или иначе использующих ARexx, непрерывно увеличивается. Практически все новые неигровые программы имеют собственный ARexx-порт. В феврале 1994 года таких программ было 370, сейчас (июнь 95-го) - около тысячи.
ОС 2 и 3 имеют специальный каталог Rexxc, где находятся базовые программы, так или иначе обслуживающие ARexx-функции системы. Все эти программы являются обычными исполняемыми файлами - командами ОС. Наиболее важной из них является RX, позволяющая исполнять программы, написанные на ARexx-e. Ее параметром может быть:
- Имя текстового ARexx-файла, который будет интерпретироваться подобно тому, как ОС интерпретирует командные файлы Амиги. Пример использования:
rx Make.rexx
- Некоторая командная строка, содержащая одно или несколько утверждений ARexx-a. Отдельные утверждения в этом случае отделяются друг от друга символом ";".
Другие программы каталога Rexxc используются в следующих случаях:
НI посылает "стоп-сигнал" всем работающим ARexx-программам (таким образом, можно например "выскочить" из зациклившейся программы во время ее отладки).
RXC полностью прекращает работу ARexx-сервера системы после окончания работы последней из ARexx-программ.
RXSET позволяет менять значения глобальных переменных ARexx-a в так называемом клип-списке.
ТСС, ТСО, ТЕ, TS управляют отладочным режимом исполнения ARexx-программ, когда последовательно выводится информация обо всем, что происходит во время их работы.
WaitForPort после запуска ожидает открытия некоторой программой своего связного порта в течение 10-ти секунд. В случае неудачи возвращает уровень ошибки 5 (WARN). Если на момент исполнения программа WaitForPort порт уже открыт, она немедленно возвращает ноль. Обычно используется в тех случаях, когда ARexx-программа хочет выяснить, является ли активной некоторая программа.
Применение WaitForPort попробуем объяснить на примере. Допустим, мы хотим, чтобы Амига сказала с помощью русификатора по русски "Здравствуйте", причем мы заранее не знаем, работает ли в системе русификатор или нет. Вот текст соответствующей ARexx-программы с комментариями:
/* Первая строчка любой ARexx-программы всегда содержит
комментарий */
if ~show('PORTS', 'Rusifier.port') then /* Порт */
/* русификатора уже открыт? */
do
address command /* Нет, хозяин теперь ОС Амиги */
rusifier /* Запускаем русификатор, */
/* если он есть в системе */
'WaitForPort Rusifier.port' /* Ждем открытия */
/* порта русификатора... */
if rc = 5 then /* Дождались? */
do
say 'Не могу запустить русификатор...' /* Нет, */
/* не дождались... */
exit /* Конец работы программы */
end
end
address 'Rusifier.port' /* Русификатор работает! */
/* Он теперь хозяин */
'SPEAK Здравствуйте!' /* Посылаем ему нашу команду */
Поскольку ARexx, "по умолчанию", - интерпретируемый язык, то нет необходимости писать отдельные программные файлы, чтобы заставить его исполнить какое-либо элементарное действие. Если мы, например, знаем что русификатор работает, то можно просто набрать в shell-окне:
rx "address 'Rusifier.port'; 'SPEAK Здравствуйте!'"
и результат будет тем же самым. Вот что произошло в этом случае:
-
ОС запустила на исполнение интерпретатор ARexx-a - с помощью программы rх, передав ей в качестве параметра весь остаток строки. По виду своего параметра rх понял, что это явно не маршрут файла, который просят исполнить, а посему надо бы попробовать проинтерпретировать строку, как последовательность утверждений ARexx-a. При разборе содержимого этой строки интерпретатор первым делом натолкнулся на слово "address". Интерпретатор знает, что за этим известным ему словом (иначе называемым "токеном") должно последовать имя хозяина, чье дело - разбираться с теми утверждениями, которые сам интерпретатор не понимает. Точка с запятой сообщила интерпретатору, что первое утверждение закончилось, надо исполнить то, что там сказано. Интерпретатор стал искать в системе связной порт по имени Rusifier.port (кстати, в имени порта нельзя менять регистр букв!) и, найдя его, запомнил координаты порта, как почтового ящика для нового хозяина.
-
Tеперь пришла пора разбираться со следующим по счету утверждением. О слове SPEAK интерпретатору ничего не известно - такого токена в ARexx-e нет. Поэтому интерпретатор принимает решение,- передать все непонятное для него утверждение на разбор текущему хозяину, которым на данный момент времени является русификатор. В связной порт русификатора немедленно посылается сообщение, указывающее на строку "SPEAK Здравствуйте!".
-
Далее интерпретатор "ложится спать", попросив, чтобы русификатор разбудил его, как только тот разберется с присланной строкой. Русификатор прекрасно знает, что надо делать, когда тебя просят поздороваться и незамедлительно пытается исполнить поручение.
-
Если при этом у русификатора не возникло никаких проблем, то он возвращает интерпретатору нулевой код ошибки, в противном случае код ошибки будет 10 или 20 (в зависимости от степени серьезности постигшей русификатор неудачи).
-
Ответ русификатора пробуждает интерпретатор и тот смотрит, какой код ошибки вернул ему хозяин. Если ничего фатального не произошло, то интерпретация продолжается со следующего утверждения. Поскольку в данном случае текст закончился, интерпретатор прекращает свою работу и возвращает управление shell-процессу, из которого был вызван.
Столь многословное описание дано с той целью, чтобы вы смогли прочувствовать общие механизмы работы ARexx-a в многозадачном окружении. Сам интерпретатор, разумеется, может исполнять любое число ARexx-программ одновременно.
Интерпретаторы вообще (а интерпретатор ARexx-a в частности), делают свое дело (исполняют программы), прямо скажем, не быстро. Впрочем, можно говорить о чистом эффекте "ARexx-торможения" только в случае работы больших интерпретируемых ARexx-программ, которые мало обращаются к системным библиотекам и все действия (особенно циклические) выполняют, используя "исконные" ARexx-утверждения.
Для справки: на модели А4000/040 (25 мгц) пустой цикл вида
do 100000 end
выполняется около 8 секунд (разумеется, в режиме интерпретации).
Для того, чтобы программировать на ARexx-e, не обязательно быть программистом. Образно говоря, ARexx - это рояль, на котором чаще всего играют одним пальцем. Элементарные действия и выполняются элементарно просто - даже неподготовленному пользователю достаточно нескольких минут, чтобы уже что-то получилось. 99% существующих ARexx-программ имеют в длину менее 99-ти строчек и отлаживаются в течение максимум 99-ти секунд (конечно, если вы не задумали или вам не заказали нечто эпохальное - например, автору этой книги однажды на полном серьезе предложили написать программу, воспринимающую с голоса турецкий язык и мгновенно говорящую то же самое по-грузински).
Автор, будучи сам апологетом языка Forth, но работающий исключительно на С (к сожалению. Forth на Амиге как-то не прижился), отнюдь не призывает создавать большие проекты с помощью ARexx-компиляторов. В конце концов, это дело вкуса.
Одно из основных отличий ARexx-a от других языков программирования заключается в том, что переменные ARexx-a не имеют типа; никаких предварительных описаний переменных нет. Значение переменной можно использовать в качестве числа, или строки символов - как вам угодно. Например, пусть значением переменной "s" будет строка "123":
s = '1231' /* Апострофы указывают границы строки */
Комментарии заключаются, как и в языке "С" между /* ... */. Поскольку и нам, и ARexx-y ясно, что 123 является числом, то ничто не мешает исполнить:
у = s + 2
А теперь вспомним, что '123' - строка и исполним:
say left(s,1) ==> 1
Здесь и далее по тексту символы "==>" означают, что часть строки справа от них является результатом работы части строки слева от них. Оператор say выводит значение некоторого выражения, (аналог PRINT языка BASIC), функция left(s,n) возвращает подстроку строки "s" длиной "n" символов, чье начало совпадает с началом исходной строки.
Многим пуританам от программирования подобная свобода обращения с переменными покажется не слишком симпатичной, однако ARexx работает именно так, хотим мы этого или нет.
Типы данных
ARexx работает с четырьмя типами данных, обобщенно называемых символами:
Фиксированный символ может состоять из цифр 0-9, десятичной точки, экспоненты "Е", а также предшествующего знака + или -., Фиксированные символы - по сути числа, всегда являющиеся константами. Кстати, единственным определением константы в ARexx-e служит то, что она не может стоять в левой части оператора присваивания.
ARexx воспринимает числа, как десятичные. Если мы хотим ввести шестнадцатеричное число, мы должны ввести его, как строку (т.е. заключить в апострофы) и добавить после закрывающего апострофа дескриптор "х" (дескриптором двоичных чисел является "b"):
123 - десятичное число
'CD9'x - шестнадцатеричное число может включать цифры 0-9 и буквы A-F.
'110'b - двоичное число состоит только из нулей и единиц.
Любая последовательность литер (мы сознательно используем синоним слова "символ" - литера, чтобы не было путаницы с символами ARexx-a), заключенная в апострофы или кавычки, считается литеральной строкой, по сути являющейся константой.
Это аналоги простых переменных в других языках программирования. Имя простого символа не может начинаться с числа и иметь где-либо внутри себя точку. Любопытным свойством является то, что если не присваивать никакого значения простому символу, то при первом же его использовании этим значением станет его собственное имя в заглавном регистре. Например:
say hello ==> HELLO
hello = 1
say hello ==> 1
say 'hello' ==> hello (литеральная строка, а не имя простого символа)
Аналог массивов, существующих практически во всех языках программирования. Имя стем-символа (или просто стема) всегда завершается точкой. Пример:
array. = 10
Подобное присвоение приведет к тому, что при считывании любого элемента стема, будет возвращено значение 10.
Эти символы содержат по крайней мере одну точку внутри своего имени. По сути являются обращением к одному из элементов стема. Примеры:
array. = 10 /* Обращение к любому не */
array.1 = 'Первый элемент' /* инициализированному */
/* элементу теперь даст */
/* значение 10 */
say array.1 ==> Первый элемент
say array.3 ==> 10
say array.9 ==> 10
this = 5
array.this = 2
say array.this ==> 2 /* Простой символ */
/* является индексом */
say array.1.1 ==> 10 /* Двумерный массив */
Операторы
Операторы ARexx-a подразделяются на 4 типа: арифметические, конкатенации, сравнения и логические. Очередность исполнения операторов в выражении зависит от приоритета оператора, который выражается некоторым фиксированным для этого оператора числом. Для изменения приоритета используются круглые скобки. Операторы делятся на унарные (используют единственный операнд) и бинарные (требуют двух операндов).
Ниже дан список арифметических операторов ARexx-a.
Имя Смысл Приоритет Подтип
+ преобразование 8 Унарный
- изменение знака 8 Унарный
** возведение в степень 7 Бинарный
* умножение 6 Бинарный
/ деление 6 Бинарный
% целочисленное деление 6 Бинарный
// модуль (остаток от деления) 6 Бинарный
+ сложение 5 Бинарный
- вычитание 5 Бинарный
Операции возведения в степень, целочисленного деления и определения модуля требуют целочисленных операндов.
Операторы конкатенации (слияния строк)
Единственным явным оператором конкатенации в ARexx-e служит двойная вертикальная черта "||". Пример:
say 'Ком' || 'пьютер' ==> Компьютер
Один или несколько пробелов между литеральными строками также вызовут конкатенацию этих строк, однако между ними будет при этом вставлен единственный пробел:
say 'Здравствуйте' 'товарищи!' ==> Здравствуйте товарищи!
Поэтому пробел в ARexx-e также считается оператором конкатенации. Попытка исполнить это же утверждение без пробела между литеральными строками приведет к тому, что ARexx истолкует двойной апостроф в качестве требования включить апостроф в полученную строку:
say ' Здравствуйте ''товарищи !' ==> Здравствуйте'товарищи!
Слияние литеральных строк со значениями переменных происходит без включения в результирующую строку дополнительных пробелов:
word = 'ход'
say 'napo'word ==> пароход
Результат в данном случае будет тем же, как и при использовании оператора ||.
Сравнение происходит либо между числами, либо между строками. Сравнение может быть строгим (strict) или обычным. Строгое сравнение подразумевает полное совпадение сравниваемых объектов. При обычном сравнении не учитываются предшествующие и/или последующие пробелы при сравнении строк, а также незначащие нули при сравнении чисел. Сравнение будет числовым, если оба сравниваемых операнда являются числами, в противном случае операнды будут сравниваться, как строки. Строгие сравнения всегда являются строчными. Результатом сравнения могут являться значения ИСТИНА (TRUE), что соответствует единице, либо ЛОЖЬ (FALSE), что соответствует нулю.
Имя Смысл Приоритет Подтип
= Равно 3 Обычный
== Точное равенство 3 Строгий
~= Не равно 3 Обычный
~== Точное неравенство 3 Строгий
> Больше 3 Обычный
~> Не больше 3 Обычный
< Меньше 3 Обычный
~< Не меньше 3 Обычный
>= Больше или равно 3 Обычный
<= Меньше или равно 3 Обычный
Результатом работы любого из логических операторов является либо значение ИСТИНА, равное единице, либо значение ЛОЖЬ, равное нулю.
Имя Смысл Приоритет Подтип
~ Отрицание 8 Унарный
& Конъюнкция (И) 2 Бинарный
| Дизъюнкция (ИЛИ) 1 Бинарный
&& Исключающее ИЛИ 1 Бинарный
^ Исключающее ИЛИ 1 Бинарный
Инструкции
Инструкции ARexx'a состоят из ключевого слова, за которым могут следовать один или несколько параметров. Вертикальная черта | означает выбор одной из нескольких альтернатив, квадратные скобки - необязательный параметр. Угловые скобки означают обязательный параметр.
ADDRESS | <имя> [командное выражение] | [VALUE <выражение, вычисляющее имя хозяина>] |;
Инструкция ADDRESS позволяет выбрать нового текущего хозяина или послать указанному параметром <имя> хозяину результат вычисления некоторого выражения.
Имя хозяина является именем его ARexx-порта. В этом имени регистр букв менять нельзя, например для русификатора имя должно быть 'Rusifier.porf, но никак не 'RUSIFIER.PORT. Имя должно быть литеральной строкой, заключенной в апострофы или кавычки - при использовании значения переменной в качестве имени хозяина необходим параметр VALUE. Специальным именем хозяина является COMMAND - при этом результат будет аналогичен запуску последующих ОС-команд из cli (shell). При использовании VALUE командное выражение не может присутствовать в качестве параметра - в этом случае вычисляется имя нового текущего хозяина.
Если кроме имени хозяина присутствует также командное выражение, то замены текущего хозяина не произойдет, а просто указанному хозяину будет послан результат вычисления выражения.
ARexx всегда помнит двух хозяев - предыдущего и текущего. Если исполнить ADDRESS без параметров, то предыдущий хозяин станет текущим (а текущий, разумеется, предыдущим).
Пример использования - выводим текущий каталог из ARexx-a:
ADDRESS 'COMMAND'
DIR
При первоначальном запуске ARexx-программы с помощью команды RX текущим хозяином становится порт интерпретатора по имени REXX. Этот хозяин умеет делать единственное дело: синхронно запускать на исполнение другие ARexx-программы. Когда ему посылается команда, он ищет файл с именем, соответствующим имени команды, вначале в текущем каталоге, затем в каталоге REXX:. В обоих случаях сначала ищется файл, точно соответствующий указанному имени, а в случае неудачи - файл с тем же именем, но расширением ".гехх". ARexx-файл, запустивший на исполнение другой ARexx-файл. послав команду REXX-хозяину, всегда ждет, пока "сыновний" файл закончит работу (это и есть синхронный запуск). Остаток строки после имени запускаемого файла может содержать аргументы для него.
Еще один всегда готовый к работе хозяин по имени "AREXX" во всем подобен "REXX"-y, однако запускает ARexx-программы асинхронно (т.е. основная ARexx-программа, обратившаяся с просьбой о запуске другой ARexx-программы к хозяину "AREXX", не ждет, пока новая программа закончит работу). Если надо, новая программа может использовать функцию OPENPORT() и начать обмениваться различными сообщениями с кем угодно, в том числе и с "программой-родителем",
ARG <образец>
Сокращение от PARSE UPPER ARGS <образец>. Инструкция позволяет использовать аргументы при запуске программы или вызове функции. Подробности см. в описании PARSE.
BREAK
Выход из DO...END блока или из интерпретируемой (см. инструкцию INTERPRET) строки.
CALL <имя> [выражение] [[,] выражение] ...
Вызов функции, подпрограммы или запуск другой ARexx-программы. Имя должно быть литеральной строкой - имена переменных здесь не допускаются. Имя соответствует имени функции, ARexx-программы или метке подпрограммы.
При вызове функции возможные аргументы могут быть заключены в необязательные круглые скобки. После возврата из вызванной подпрограммы (функции), значение, возможно возвращенное вызванной подпрограммой (функцией), присваивается переменной RESULT. Кроме того, при вызове внутренней подпрограммы (функции), переменной SIGL присваивается значение, равное номеру строки программы, в которой находится инструкция CALL.
DO
Определяет группу инструкций в качестве единого блока инструкций. DO может быть идентификатором начала целой серии программных структур. Каждой открывающей блок инструкции DO должна соответствовать закрывающая блок инструкция END. Допускается неограниченное вложение DO...END конструкций. Ниже демонстрируются возможные варианты использования DO...END:
DO /* Простейший тип блока, обычно */
инструкции... /* употребляемый в конструкциях */
END /* типа IF...THEN...ELSE */
DO 5 /* Выполнить инструкции блока 5 раз */
инструкции... /* Разумеется, вместо цифры 5 может */
END /* стоять имя переменной */
DO i = 1 TO 5 BY 2 /* Переменная i последовательно при- */
инструкции... /* нимает значения от 1 до 5 с шагом 2 */
... /* При отсутствии BY шаг изменения */
END /* i будет равен единице. */
DO i = 1 TO x FOR 5 /* Выход из цикла произойдёт либо */
инструкции... /* после достижения переменной i */
... /* значения x, либо после того, как */
END /* цикл будет исполнен 5 раз */
DO WHILE x < limit /* Исполнять, пока значение перемен- */
инструкции... /* ной x меньше значения переменной */
... /* limit. Цикл может вообще не ис- */
END /* полняться, если сразу x~< limit. */
DO UNTIL x < limit /* Аналогично WHILE, однако проверка */
инструкции... /* выполняется не перед исполнением */
... /* тела цикла, а после него, поэтому */
... /* цикл всегда будет исполнен хотя бы */
END /* 1 раз */
DO FOREVER /* Бесконечный цикл. Выйти из */
инструкции... /* цикла можно с помощью ин- */
... /* струкций LEAVE или BREAK. */
IF e = f THEN ITERATE /* Исполнение EXIT приведет к */
IF x = у THEN BREAK /* прекращению работы програм- */
IF a = b THEN LEAVE /* мы. ITERATE вызывает игно- */
IF с = d THEN EXIT /* рирование оставшейся части */
END /* цикла и переход на его начало (только */
/* в том случае, если конструкция DO...END */
/* является циклической, а не просто блоком; */
/* это касается и LEAVE) */
DROP <имя>
Присваивает переменной с заданным именем ее изначальное значение - например, переменная Var1 после исполнения DROP Var1 будет иметь значение VAR1.
ECHO
То же, что SAY (см. описание ARexx-инструкции SAY).
EXIT [значение]
Эта инструкция прекращает исполнение ARexx-программы. Параметром для EXIT может быть код возврата, соответствующий ОС-овскому уровню ошибки (см. описание ОС-команды FAILAT).
IFTHEN ... [ELSE]
Инструкция осуществляет управление ветвлениями в программе в зависимости от результата выражения ехрr. Если результат получится ненулевой, будет исполняться инструкция (блок инструкций) после THEN. Если результат нулевой и присутствует ключевое слово ELSE, то будет выполняться инструкция (блок инструкций) после ELSE. Примеры:
IF x > 1 THEN SAY x
IF x ~= 0 THEN
SAY 'x не равно нулю'
ELSE
SAY 'x равно нулю'
Поскольку ELSE всегда соответствует ближайшему IF, но использование ELSE не является необходимостью, иногда приходится вводить фиктивные ELSE для правильной логики работы всей конструкции:
IF x > 5 THEN
IF x = 7 THEN
say 'x равно семи'
ELSE
NOP
ELSE
say 'x меньше или равно пяти'
Если бы мы в этом примере не использовали дополнительного ключевого слова ELSE, то последнее ELSE относилось бы не к первому IF, как требуется по логике примера, а ко второму. Поскольку после ELSE в любом случае должна следовать некоторая инструкция (блок инструкций), мы были вынуждены использовать "пустую" инструкцию NOP. которая ничего не делает.
INTERPRET <выражение>
Обрабатывает выражение, затем исполняет его в качестве инструкции ARexx-a. Результатом выражения должна явиться строка, содержащая одно или несколько утверждений ARexx-a (если утверждений несколько, то они должны быть отделены друг от друга символом ";". Пример:
Part1 = 'SAY'
part2 = 'Привет!'
part3 = 'Bye!'
INTERPRET part1 part2 ';' part1 part3 ==> Привет! Bye!
Мы сознательно приводим наипростейший пример - на самом деле возможности INTERPRET очень велики.
NOP
Пустая операция, пример использования которой см. в описании IF.
NUMERIC | DIGITS <выражение> | FUZZ <выражение> | FORM | [SCIENTIFIC] | [ENGINEERING] | [[VALUE] &60;выражение>]
Определяет точность чисел используемых при вычислении выражений, а также точность возвращаемых результатов. Если количество цифр какого-либо числа превышает величину, заданную инструкцией NUMERIC, то производится округление этого числа до значения, содержащего заданное по NUMERIC число значащих цифр.
NUMERIC DIGITS определяет точность расчета арифметических выражений (по умолчанию она равна 9 значащим цифрам). Выражение, определяющее требуемую точность, должно возвращать значение от 1 до 14. Чтобы узнать текущую точность вычислений, достаточно исполнить встроенную функцию DIGITS().
NUMERIC FUZZ задает точность сравнения чисел. Выражение, определяющее требуемую точность, должно возвращать значение меньшее, нежели текущая точность расчета. Чтобы узнать текущую точность сравнения чисел, достаточно исполнить встроенную функцию FUZZ(). Нулевое значение параметра означает, что точность сравнения соответствует значению NUMERIC DIGITS.
NUMERIC FORM определяет формат вывода, когда число представлено в экспоненциальной форме - научный (SCIENTIFIC) или инженерный (ENGINEERING). По умолчанию принят научный формат - мантисса числа больше 0 и меньше 1. При инженерной нотации порядок (степень десяти) представлен максимальным числом, нацело делящимся на 3. Выражение (если оно есть) должно возвращать соответствующую требуемой нотации строку - т.е. SCIENTIFIC или ENGINEERING. Ключевое слово VALUE можно не употреблять в том случае, если выражение начинается не с символа и не с литеральной строки. Использование NUMERIC FORM без дополнительного выражения, восстанавливает нотацию, принятую по умолчанию (т.е. научную). Чтобы узнать, какой является текущая нотация, достаточно исполнить функцию FORM().
OPTIONS | RESULTS [ON | OFF] | PROMPT <выражение> [ON | OFF] | FAILAT <выражение> [ON | OFF] | CACHE [ON | OFF]
Эта инструкция меняет некоторые установки, касающиеся текущей исполняемой ARexx-программы. Дополнительные переключатели ON и OFF могут использоваться для включения/выключения заданной опции (по умолчанию при исполнении OPTIONS заданная опция всегда включается, так что использовать ON нет абсолютно никакой необходимости).
OPTIONS RESULTS заставляет ARexx присваивать переменной RESULT значение строки, зачастую возвращаемое многими внешними программами (командами ОС).
OPTIONS PROMPT определяет строку подсказки, используемую в таких инструкциях, как PULL, PARSE PULL и PARSE EXTERNAL. Подсказка является строкой, полученной в результате вычисления выражения параметра OPTIONS PROMPT. Эта строка должна иметь вид, позволяющий вывести ее в консольное (т.е. shell-) окно и может содержать управляющие коды, меняющие, например, ее цвет или форму символов.
OPTIONS FAILAT определяет уровень ошибки, возвращенный внешней программой, который будет рассматриваться ARexx-интерпретатором в качестве фатального, т.е. вызовет прерывание исполнения текущей ARехх-программы. По умолчанию этот уровень наследуется из ОС (используется уровень, существовавший на момент запуска ARexx-программы); в ОС для изменения уровня фатальной ошибки служит команда FAILAT (см. описание FAILAT). OPTIONS FAILAT определяет уровень срабатывания ловушки, возможно установленной с помощью инструкции SIGNAL ON FAILURE.
Опция OPTIONS CACHE no умолчанию является включенной; ее действие заключается в буферизации повторяющихся ARexx-инструкций, что ускоряет интерпретацию программы. Отключать OPTIONS CACHE нет никакого смысла - много памяти вы на этом не выиграете, а исполнение ARexx-программы может существенно замедлиться.
PARSE [UPPER] | ARG <список переменных&62; | EXTERNAL <список переменных> | NUMERIC <список переменных> | PULL <список переменных> | SOURCE <список переменных> | VALUE <выражение> WITH <список переменных> | VAR <список переменных> | VERSION <переменная>
Инструкция присваивает перечисленным в списке переменным значения аргументов, полученные из некоторого источника. Что это за источник, определяет одно из дополнительных ключевых слов. Необязательное ключевое слово UPPER может предварительно преобразовать все строчные буквы аргументов-строк в заглавные. Если необходимо "перепрыгнуть" через один или несколько аргументов, то вместо имен переменных, соответствующих этим аргументам, в списке используют точки (точки должны отделяться пробелами от любых других имен).
Ключевое слово ARG указывает на то, что аргументы определены при запуске текущей программы (или вызове функции). Синонимом инструкции PARSE UPPER ARG является просто ARG. Пример использования - наша ARexx-программа называется Sample.гехх и вызывается из shell строкой:
rx Sample.гехх Привет 1 2
А вот текст самой программы, а заодно и результаты ее работы:
/* Первая строчка любой ARexx-программы */
/* всегда содержит комментарий */
PARSE ARG first, second, third
say first ==> Привет
say second ==> 1
say third ==> 2
Следует отметить, что библиотека RexxDosSupport.Iibrary (автор Хартмут Гебель) позволяет использовать в ARexx-программе мощнейшие механизмы ОС Амиги для манипуляции с аргументами, передаваемыми функции (программе), что резко упрощает задачу ARexx-программиста по написанию процедур обработки этих аргументов.
PARSE PULL выуживает аргументы из стандартного потока ввода (Stdln), который, как правило, "вливается" в ARexx-программу из материнского shell-окна. Если какие-либо данные были отправлены во входной поток инструкциями PUSH или QUEUE, то PARSE PULL достает их оттуда согласно порядку их расположения. Если входной поток пуст, то будет ожидаться ввод пользователем данных с клавиатуры, который должен завершиться нажатием клавиши Enter. PULL можно использовать в качестве самостоятельной инструкции, которая работает аналогично PARSE UPPER PULL. Для определения подсказки, появляющейся в shell-окне при ожидании ARexx-программой ввода пользователя, служит инструкция OPTIONS PROMPT.
PARSE EXTERNAL работает подобно инструкции PARSE PULL однако используется поток ввода StdErr (а не Stdln). Поток этот явно определен только в случае, если включена трассировка ARexx-программы и на экране присутствует трассировочное окно. Можно определить поток StdErr, как исходящий из текущего консольного окна, исполнив утверждение:
call open(STDERR,"*",'R')
Ключевые слова NUMERIC, SOURCE и VERSION позволяют получить доступ к информации о текущей ARexx-программе и системе, в которой она работает.
PARSE NUMERIC присвоит требуемой переменной строку, характеризующую текущий режим обработки чисел - точность операций, точность сравнения и выбранный формат вывода для чисел с плавающей точкой. Пример:
PARSE NUMERIC Nums
SAY Nums ==> 9 0 SCIENTIFIC
Первым в строке находится параметр, соответствующий установке NUMERIC DIGITS, затем - NUMERIC FUZZ, затем - NUMERIC FORM (см. описание инструкции NUMERIC).
PARSE SOURCE присваивает требуемой переменной строку, характеризующую среду, из которой была вызвана текущая ARexx-программа. Формат полученной строки:
<тип> <результаты> <вызвана> <маршрут> <расширение> <хозяин>
"Тип" соответствует тому, как была вызвана текущая программа - в качестве ARexx-программы или внешней функции. Соответственно токен типа может принимать значения COMMAND или FUNCTION.
Токен "результаты" может принимать логические значения "Истина" (единица), либо "Ложь" (ноль), в зависимости от того, включен ли режим OPTIONS RESULTS.
"Вызвана" соответствует имени этой ARexx-программы или функции; "маршрут" соответствует ее полному маршруту; "расширение" - дополнительному расширению, с учетом которого производится поиск файлов в случае неудачного поиска заданного файла без расширения. По умолчанию это расширение "REXX" (точка, отделяющая имя от расширения, здесь не указывается).
"Хозяин" - изначальный хозяин программы - то же, что результат исполнения функции ADDRESS() на момент старта программы. Следует учесть, что при запуске внешней функции или ARexx-программы из другой ARexx-программы хозяин "наследуется"; команда же RX устанавливает хозяина REXX.
PARSE VERSION предоставляет общую информацию о конфигурации системы, в которой ARexx-программа имеет честь работать. Формат полученной строки:
<язык> <версия> <процессор> <сопроцессор> <видеорежим> <кадровая_частота>
"Язык" всегда будет "AREXX", поскольку все происходит на Амиге. На других компьютерах вы могли бы получить что-нибудь типа "REXX370" или "REXXSAA".
"Версия" соответствует текущей версии ARexx-интерпретатора вашей системы. На июнь 1995 года последней версией является 1.15.
"Процессор" соответствует типу центрального процессора вашей системы. Версия 1.15 ошибочно сообщает пользователям 68040, что в системе стоит процессор 68070, о котором даже сама фирма Motorola ничего не знает.
"Сопроцессор" соответствует типу математического сопроцессора вашей системы (разумеется, если он есть - иначе будет выведено "NONE"). В случае, если в вашей системе установлен центральный процессор 68040 со встроенным математическим сопроцессором, вам будет сообщено о наличии 68881-го сопроцессора, что не совсем соответствует действительности.
"Видеорежим" может принимать 2 значения - NTSC и PAL.
"Кадровая частота" может быть "50HZ" или "60HZ". Эти два сообщения могут иметь весьма сомнительное отношение к реальному экранному режиму AGA-компьютеров (А1200 и А4000).
PARSE VALUE и PARSE VAR позволяют использовать не внешние, а внутренние данные в качестве опрерандов для PARSE. PARSE VAR использует значение единственной переменной, a PARSE VALUE - значение некоторого выражения, после которого должно следовать обязательное ключевое слово WITH, а далее - список переменных. Разумеется, выражение может состоять из единственного имени переменной, однако в этом случае проще использовать PARSE VAR. Примеры использования:
PARSE VALUE TIME() WITH Hours ':' Min ':' Sec
SAY Hours 'часов' Min 'минут' Sec 'секунд'
==> 12 часов 11 минут 37 секунд
Если выражение отсутствует, то всем переменным списка будут присвоены нулевые значения. Таким образом можно использовать PARSE VALUE для обнуления сразу нескольких переменных.
PROCEDURE [EXPOSE <переменная> [<переменная>] [...]]
Эта инструкция создает новую символьную таблицу для встроенной функции. В символьную таблицу входят имена переменных, доступных из этой функции. Без использования инструкции PROCEDURE все переменные вызывающей функцию программы доступны из нее без каких-либо ограничений, т.е. ничто не мешает менять их из тела функции. Пример:
х = 1
call func
say x ==> 2
exit
func:
x = 2
return
Ситуация меняется при использовании инструкции PROCEDURE, локализирующей переменные, действия с которыми происходят в теле функции:
х = 1
call func
say x ==> 1
exit
func: PROCEDURE
x = 2
return
В этом случае переменная х создается функцией заново и не имеет никакого отношения к глобальной переменной х, созданной в основной программе. Если же какие-либо переменные основной программы и в этом случае должны быть доступны из тела функции, то их имена следует явно указать после ключевого слова EXPOSE:
X = 1
У = 3
call func
say x ==> 2
say у ==> 3
exit
func: PROCEDURE EXPOSE x
x = 2
у = 4
return
PULL Сокращение от PARSE UPPER PULL (см. описание PARSE).
PUSH <выражение>
Кладет результат вычисления выражения с добавленной литерой "перевод строки" (ASCII значение 10) во входной поток Stdln. Порядок помещения данных во входной поток при этом соответствует стековому принципу LIFO (Last In First Out) - "последний ушел - первый вышел". Позже все что было положено во входной поток, может быть оттуда вынуто инструкцией PARSE PULL. Если на момент окончания работы ARexx-программы входной поток не будет пустым, то ОС Амиги попытается исполнить все лежащие в нем строки одну за другой, как если бы их набирали в shell-окне с клавиатуры. Для выяснения количества имеющихся строк во входном потоке служит встроенная функция LINES().
QUEUE <выражение>
Инструкция работает аналогично PUSH, но укладка строк во входной поток происходит по принципу FIFO (First In First Out) - "первый ушел - первый вышел", т.е. первая положенная строка будет использована первой при последующем чтении входного потока.
RETURN [<выражение>]
Используется внутри функций для возврата управления в точку, следующую за точкой вызова функции. Значение выражения является величиной, возвращаемой функцией.
SAY [<выражение>]
Выводит результат вычисления выражения с добавленной литерой "перевод строки" (ASCII значение 10) в выходной поток StdOut (как правило, этот поток направлен в shell-окно, из которого ARexx-программа была запущена на исполнение). Синонимом SAY является ECHO.
SELECT
Выбор одного из альтернативных ветвлений. Пример:
SELECT
WHEN x = 1 THEN
SAY 'x равно единице'
WHEN x < 5 THEN
DO
y = y + 1
SAY 'x меньше пяти'
END
OTHERWISE
SAY 'x не меньше пяти!'
END
Если результат вычисления выражения, следующего после WHEN не равен нулю, то выполняется инструкция (блок инструкций) между соответствующим THEN и последующим WHEN (OTHERWISE, END). Необязательное ключевое слово OTHERWISE "срабатывает" только в том случае, если не "сработало" ни одно из WHEN.
SIGNAL
Эта инструкция имеет двоякое значение. При использовании вместе с ключевыми словами ON или OFF, SIGNAL вызывает включение (ON) или выключение (OFF) явно определяемой реакции на возникновение какой-либо исключительной ситуации в системе. Если для заданной ситуации было исполнено SIGNAL ON, то в случае ее возникновения произойдет безусловный переход на некоторую подпрограмму. Ситуация, на которую предполагается отреагировать, ассоциируется с одним из нижеследующих резервированых слов (имя вызываемой подпрограммы должно совпадать с соответствующим резервированным словом):
BREAK_C получение сигнала останова от ОС (см. описание
BREAK_D ОС-команды BREAK), также при нажатии соответствующей
BREAK_E комбинации клавиш (например, CTRL-C или CTRL-D);
BREAK_F
ERROR команда вернула ненулевой код ошибки;
FAILURE команда вернула код ошибки более 19-ти;
HALT получен общий сигнал останова (см. описание команды HI);
IOERRR ОС сигнализирует об ошибке ввода-вывода:
NOVALUE использование значения неинициализированной переменной;
SYNTAX синтаксическая ошибка либо ошибка периода исполнения.
ARexx по умолчанию (т.е. без исполнения SIGNAL ON) реагирует на некоторые из вышеперечисленных сигналов следующим образом:
BREAK_C, HALT, SYNTAX - исполнение программы прерывается.
FAILURE - выводится сообщение об ошибке; исполне-
ние программы прерывается.
Все остальные исключительные ситуации по умолчанию игнорируются. Если возникла одна из ситуаций, на которую ARexx должен отреагировать, то системная переменная ARexx-a SIGL будет содержать номер строки, во время исполнения которой эта ситуация произошла. Пример использования SIGNAL с ключевым словом ON:
SIGNAL ON ERROR /* Реагировать на ненулевой уровень ошибки */
... /* Здесь находится собственно ARexx-программа */
exit
ERROR: /* Подпрограмма реакции на ошибку */
SAY 'Произошла ошибка в строке' SIGL
exit
Если SIGNAL используется без ключевых слов ON или OFF, то параметром должно быть или имя метки, на которую происходит передача управления, или должно следовать ключевое слово VALUE, затем выражение, вычисляющее имя той же метки. Подобная конструкция практически то же самое, что и оператор GOTO, который рекомендуется использовать только тогда, когда не использовать его нельзя, т.е. никогда.
TRACE | {?|!} [<опция>] | VALUE <выражение> | [-] <число>
Включает мощные отладочные средства ARexx-a. Вывод отладочной информации производится в выходной поток StdErr, который может быть открыт внутри этой же ARexx-программы. Если поток StdErr не определен, но с помощью команды ТОО (Trace COnsole) было открыто консольное окно трассировки, то поток StdErr направляется именно туда. Если и такого окна нет, то ARexx направит StdErr туда же, куда и StdOut - как правило, в текущее shell-окно.
Параметр "опция" определяет формат выводимой информации; мы дадим полные английские идентификаторы трассировочных опций ARexx-a, хотя реальные опции состоят из однобуквенных идентификаторов, являющихся первыми буквами имен указанных опций:
Аll - (все); все утверждения выводятся в консольное окно по мере их исполнения, но результаты не показываются.
Background - (фон); прекращает трассировку, причем даже внешний запрос на начало трассировки (команда TS) после Background не будет иметь эффекта.
Commands - (команды); трассируются только командные утверждения.
Errors - (ошибки); выводятся командные утверждения, чье исполнение вызвало ненулевой уровень ошибки; также выводится значение уровня ошибки.
Intermediates - (промежуточные); трассируется все, что только можно. Выводятся промежуточные результаты, получаемые при вычислении каждого из выражений вместе со значениями переменных. Идентификаторами типов выводимых данных являются:
>.> - это значение не присваивается (см. описание инструкции PARSE, где сказано об использовании точек в образцах).
>>> - результат исполнения всего утверждения в целом. Этот идентификатор используется и при других опциях трассировки.
>С> - имя комбинированной переменной;
>F> - значение, возвращенное функцией;
>L> - литеральное значение (константа);
>О> - результат бинарной операции;
>Р> - результат унарной (префиксной) операции;
>U> - имя переменной, значение которой не было присвоено;
>V> - значение переменной.
Labels - (метки); трассируются только переходы на строки, содержащие метки (например, при вызовах подпрограмм).
Normal - (обычный); этот режим трассировки устанавливается по умолчанию. При нем трассируются только утверждения, возвращающие код ошибки выше установленного текущего предела.
Off - (выключить); прекращает трассировку, которая может быть снова возобновлена после исполнения команды TS.
Results - (результаты); трассируется все, но выводятся только результаты вычисления выражений.
Scan - (сканирование); в этом режиме трассируются все утверждения без их действительного исполнения; весьма удобно использовать для предварительной проверки синтаксиса.
Если после инструкции TRACE присутствует ключевое слово VALUE, то за ним должно следовать выражение, результатом вычисления которого будет одна из вышеприведенных опций (т.е. первая буква этой опции).
Символы "!" и "?" могут быть использованы как отдельно (TRACE ?), так и в комбинации с необходимыми однобуквенными идентификаторами формата (TRACE ?). Эти символы действуют в качестве переключателей: первое их использование включает соответствующий им режим, повторное - выключает и т.д.
Символ "?" - переключатель интерактивного режима (интерактивный режим трассировки, подробно описанный в соответствующей главе, также автоматически включается при исполнении команды TS (Trace Start).
Символ "!" - переключатель режима "отключения хозяина". Если хозяин отключен, то никакие сообщения ему не посылаются, хотя все остальные действия, связанные с обработкой утверждений, предназначенных для хозяина, выполняются как обычно. При этом считается, что проблем у хозяина не возникает и он всегда возвращает нулевой код ошибки. Этот режим весьма удобен при отладке ARexx-программы без запуска программы-хозяина (хозяев).
Если параметром инструкции TRACE является отрицательное число, то трассировка не включается для числа программных строк, соответствующих абсолютному значению этого числа.
Если параметром инструкции TRACE является положительное число, то трассировка этого числа программных строк "проскакивает подряд", т.е. интерактивный режим для них отменяется.
UPPER <переменная> [<переменная>] [...]
Переводит буквы в значениях строчных переменных в заглавный регистр.