Глава 11 Первое знакомство |
Встроенный в версии ОС 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. Ее параметром может быть:
Исполнится файл Make.rexx, содержащий текст ARexx-программы.rx Make.rexx
Другие программы каталога 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 работает именно так, хотим мы этого или нет.