REXX — довольно древний, передовой для своего времени язык. Он появился, согласно википедии, в 1979 году, то есть ему недавно исполнилось 40 лет. Не самый конечно старинный язык, но возраст достойный.
Rexx это аббревиатура, означает она Restructured Extended Executor, что вероятно отсылает нас к его предкам EXEC/EXEC2. Я с ним познакомился в операционной системе VM/SP 3, где он пришел на замену именно этим скриптовым языкам. Потом он был популярен в OS/2, использовался во многих других ОС, было создано множество реализаций и производных диалектов.
Ему не слишком много внимания уделялось на Хабре, постараюсь восполнить пробелы в этой статье.
У первоначальной реализации языка был один автор, сотрудник IBM Mike Cowlishaw. Эта реализация была написана на ассемблере S/360.
Википедия считает, что на REXX повлияли PL/1, EXEC и EXEC2 и Алгол. В целом да, это язык несомненно по своему синтаксису — наследник Алгола (или Pascal, если угодно). То есть, циклы, блоки и т.п. конструкции окружаются не фигурными скобками, а ключевыми словами, do/end, например. Что же касается EXEC и EXEC2, то REXX был создан как их замена, а больше пожалуй ничего общего с ними и не имеет. Ну а по сравнению с PL/1 REXX сильно проще, да и назначение языка совершенно другое.
Как выглядит код на REXX:
Что, на мой взгляд, было самым интересным или необычным в этом языке?
1. Синтаксис языка достаточно свободный, это совсем не Фортран, разумеется. На Алгол и паскаль похоже больше всего. На basic в общем тоже.
Есть операторы, типичные для структурного программирования.
Есть функции и процедуры. Тут немного необычным является то, что процедура по умолчанию прячет все переменные от вызывающего кода, но часть из них можно сделать доступными, при помощи ключевого конструкции expose <список переменных>.
Типизация динамическая. Переменная может интерпретироваться как строка или как число, в разные моменты выполнения, а также может быть не определена (это можно проверить, хоть и кривовато). Фактически, в языке один примитивный тип данных — строки, над которыми можно проводить арифметические операции, если строка представляет собой правильное число.
Вот маленький примерчик из википедии:
Отсюда видно, что неопределенная переменная в первой (и последней) строке вычисляется в свое имя прописными буквами. Это и есть способ проверки на неопределенность.
Оператор interpret — это аналог того, что сегодня обычно зовут eval.
Обработка ошибок пожалуй больше всего похожа на Basic:
Это обработка завершения программы (кнопкой с терминала). Кроме halt, есть и другие «сигналы», в том числе, например, syntax (синтаксическая ошибка в интерпретируемом коде). Не помню, чтобы я это практически применял, но вполне логично проверять на синтаксическую правильность например то, что подается на вход interpret.
2. В нем не было регулярных выражений, как скажем в Perl (до Perl впрочем еще оставалось почти 10 лет). Сама концепция регулярных выражений появилась раньше, и они уже несомненно были, скажем, утилита grep уже существовала. Вместо них был оператор parse, который является некоторым упрощенным аналогом регулярок, с подстановкой групп в переменные.
Т.е. анализируем содержимое источника (который мог быть аргументом функции, переменной, выражением и т.п.) на соответствие шаблону. Шаблон мог содержать литералы, разделяющие значения, и переменные, которые получали значение из источника между этими разделителями. В каком-то смысле это похоже на группы в регулярных выражениях.
Это, конечно же, сильно проще регулярных выражений, хотя бы потому что, не было ни метасимволов типа \d, ни повторения в виде * или +, ни альтернатив, ничего такого.
3. У Rexx был API, который позволял его использовать как язык для скриптования не только ОС (CMS), но и любой другой программы, которая этот API поддерживала. Т.е. фактически, это был язык для написания DSL. Вероятно, один из первых. При этом среда исполнения могла реализовать и предоставить интерпретатору дополнительные функции или команды, доступные в итоге программе.
Выглядело это примерно так: при запуске текстового редактор XEDIT он искал где-то на доступных дисках файл под названием PROFILE XEDIT, который содержал скрипт на REXX. Этот скрипт мог назначать функциональные клавиши, и определять дополнительные команды, доступные из XEDIT. По смыслу сегодня это ближе всего к .bashrc, например. Или его аналогу для vim.
Соответственно, процедуры и функции REXX, определенные в PROFILE XEDIT, могли выполнять команды редактора, и получать доступ к информации об открытых файлах, в том числе и к содержимому самого файла.
4. Не требовалось какого-то специального синтаксиса для передачи команд хост-среде (т.е. ОС, или скажем текстовому редактору). Вместо этого, все выражения, которые Rexx не распознал, передавались на выполнение. Таким образом, чтобы записать внутри скрипта команду ОС, достаточно было заключить ее в кавычки, например:
Команду можно было построить путем конкатенации строковых констант и переменных. В качестве символа операции конкатенации выступал просто пробел.
Чтобы команду передать в другое приложение, имелся оператор address. Например, при использовании в среде текстового редактора XEDIT, чтобы выполнить команду не XEDIT, а ОС (CMS), нужно было сделать так:
5. В CMS был реализован API для доступа к стеку команд ОС, которым в том числе и Rexx пользовался для обмена данными. Команда выше возвращала полученный список файлов в стек, откуда скрипт мог их забрать, читая по строчке как стандартный ввод. В чем-то это аналогично юниксным пайпам, но несколько отличается от них. Пайп это все-таки поток байтов, а стек в CMS — это набор строк. В общем-то, это типичное различие между идеологией Unix и ОС от IBM, где файлы в значительной степени были ориентированы на строки фиксированной или переменной (но при этом известной) длины.
6. Как часть API приложения могли иметь доступ к переменным Rexx скрипта, могли читать и изменять их. Это был еще один способ обмена данными.
7. Была реализована довольно эффективная арифметика произвольной точности. Произвольной — это значит например 100 знаков и более — на сколько памяти хватит.
8. Управление памятью — автоматическое. Хотя и имелся например, оператор drop для удаления значения переменной (ассоциативного массива или его элементов), но в любом случае, дел с указателями и размерами блоков вы не имели.
9. В качестве единственной, но при этом весьма универсальной структуры данных были ассоциативные массивы, очень похожие например на javascript. То есть, если вы пишете a.1, это элемент массива a с ключом 1. Если пишете a.b, то это элемент с ключом b.
Вот скажем процедура добавления слова в словарь:
Переменная dictionary в этом фрагменте — это наш ассоциативный массив. Процедура не возвращает этот массив, а делает его доступным извне (при помощи expose). dictionary.0 хранит число элементов в словаре (это просто соглашение, и как видите, мы его выполняем сами), dictionary.n — n-е слово словаря.
10. Средств низкоуровневого доступа к памяти или к API OC язык не имел, хотя в принципе написать для него что-то подобное было вполне возможно. Могу назвать пару примеров расширений языка, одно было предназначено для работы с базой данных SQL/DS (DB2), а второе — для обмена сообщениями между виртуальными машинами в рамках VM/SP. То есть, все что вы сможете оформить как DSL, в принципе может быть реализовано.
Кроме самой CMS сразу же поддерживал REXX основной текстовый редактор XEDIT. Он позволял писать макросы на REXX, которые можно было повесить на функциональные клавиши, или вызывать как команды, как из командной строки, так и в качестве т.н. «префиксных» команд, которые вводились рядом со строкой(строками) текста, и позволяли ей(ими) манипулировать. Например, команда d позволяла удалить строку, рядом с которой была введена, были команды копирования или перемещения, и другие. А макрос на REXX мог к примеру преобразовать строку в прописные или в строчные буквы.
Второе приложение, поддерживавшее язык на моей памяти, называлось DMS (Display Management System for CMS (DMS/CMS)), и было это средство разработки интерактивных программ, состоящих из экранных форм (panels). Из REXX можно было выбрать одну из заранее нарисованных форм, заполнить ее поля значениями, и показать на экране. После нажатия пользователем функциональной клавиши программа получала управление обратно, и переменные REXX содержали значения измененных полей. Самым близким функциональным аналогом DMS я бы назвал HTML и его формы. И по сложности (простоте) разработки это было пожалуй еще чуть проще, хотя по выразительным возможностям HTML разумеется выигрывает (например, в DMS вообще не было изображений, что впрочем объяснимо текстовыми терминалами).
Еще одно приложение, которое стоило бы упомянуть, называлось ISPF. Но это уже было сильно шире DMS, и было похоже скорее на интегрированную среду разработки, как она выглядит сейчас, с поправкой на текстовые терминалы. И да, текстовый редактор тоже программировался на REXX.
Начиная с VM/SP 6 в CMS появились средства для создания на экране терминала виртуальных окон переменного размера, и показа в них текста. Соответствующий API появился и для REXX (а точнее, это и было в первую очередь расширение REXX).
Еще одним весьма интересным применением REXX был так называемый автооператор. Это было приложение, запускавшееся в виртуальной машине оператора ОС, которое получало все сообщения, обычно приходившие на терминал оператора, и позволяло их обрабатывать программными средствами. Обработчик команд — скрипт на REXX. Таким образом можно было например дать некоторые возможности оператора многим людям, для чего эти люди просто посылали сообщения виртуальной машине оператора, а обработчик (от имени привилегированного пользователя — оператора) выполнял для них какие-то команды. Ну например, монтировал в системе тома дисков или лент.
Но такими простыми вещами возможности не ограничивались. Например, вы не можете послать сообщение пользователю, если его нет в системе. Посылаете его автооператору, он сохраняет его в очереди, а затем (получив сообщение, что пользователь вошел в систему) пересылает кому нужно. Фактически, это простая реализация электронной почты небольшим скриптом на REXX.
Я бы сказал, что с появлением REXX в VM/SP 3 мы начали писать на нем значительную часть того, что ранее писалось например на PL/1. Это достаточно мощный, и в тоже время все еще простой язык, позволяющий нормально структурировать сравнительно большие приложения. В первую очередь наверное благодаря его возможностям интеграции, когда он применялся как DSL.
Возможности выполнять запросы к базе данных (SQL/DS), отображать экранные формы, читать и писать файлы покрывают значительную часть потребностей по разработке бизнес приложений.
Очевидно, язык многим понравился. Он был в OS/2, был на Амигах, под Windows, и много где еще. Позже были Object REXX с объектными расширениями, и NetREXX для JVM. Главное — этот язык все еще живой. И в общем-то, если бы пришлось выбирать, на чем сегодня писать скрипты, то рассматривая например Rexx, bash, совершенно птичий язык cmd.exe — я бы выбрал однозначно первый. Но вот в сравнении уже с языками более новыми, такими как Perl, Python, и многими-многими другими — уже все не так очевидно. Скажем, идея Rexx о передаче команд и переменных в среду исполнения была хороша — но идея, допустим COM, с его объектной ориентированностью — все же более функциональная.
Несмотря на прошедшие 40 лет, он все еще применяется в IBM z OS, да и книжка по языку, если верить википедии, вышла совсем недавно, примерно в 2012. Для разных ОС вполне можно загрузить живые реализации, и попробовать его самостоятельно.
Rexx это аббревиатура, означает она Restructured Extended Executor, что вероятно отсылает нас к его предкам EXEC/EXEC2. Я с ним познакомился в операционной системе VM/SP 3, где он пришел на замену именно этим скриптовым языкам. Потом он был популярен в OS/2, использовался во многих других ОС, было создано множество реализаций и производных диалектов.
Ему не слишком много внимания уделялось на Хабре, постараюсь восполнить пробелы в этой статье.
Предки, истоки, авторы
У первоначальной реализации языка был один автор, сотрудник IBM Mike Cowlishaw. Эта реализация была написана на ассемблере S/360.
Википедия считает, что на REXX повлияли PL/1, EXEC и EXEC2 и Алгол. В целом да, это язык несомненно по своему синтаксису — наследник Алгола (или Pascal, если угодно). То есть, циклы, блоки и т.п. конструкции окружаются не фигурными скобками, а ключевыми словами, do/end, например. Что же касается EXEC и EXEC2, то REXX был создан как их замена, а больше пожалуй ничего общего с ними и не имеет. Ну а по сравнению с PL/1 REXX сильно проще, да и назначение языка совершенно другое.
Как выглядит код на REXX:
/* Просуммировать числа от 1 до 10 */
sum = 0
do count = 1 to 10
say count
sum = sum + count
end
say "Сумма этих чисел равна " sum"."
Особенности
Что, на мой взгляд, было самым интересным или необычным в этом языке?
1. Синтаксис языка достаточно свободный, это совсем не Фортран, разумеется. На Алгол и паскаль похоже больше всего. На basic в общем тоже.
Есть операторы, типичные для структурного программирования.
Есть функции и процедуры. Тут немного необычным является то, что процедура по умолчанию прячет все переменные от вызывающего кода, но часть из них можно сделать доступными, при помощи ключевого конструкции expose <список переменных>.
Типизация динамическая. Переменная может интерпретироваться как строка или как число, в разные моменты выполнения, а также может быть не определена (это можно проверить, хоть и кривовато). Фактически, в языке один примитивный тип данных — строки, над которыми можно проводить арифметические операции, если строка представляет собой правильное число.
Вот маленький примерчик из википедии:
say hello /* => HELLO */
hello = 25
say hello /* => 25 */
hello = "say 5 + 3"
say hello /* => say 5 + 3 */
interpret hello /* => 8 */
drop hello
say hello /* => HELLO */
Отсюда видно, что неопределенная переменная в первой (и последней) строке вычисляется в свое имя прописными буквами. Это и есть способ проверки на неопределенность.
Оператор interpret — это аналог того, что сегодня обычно зовут eval.
Обработка ошибок пожалуй больше всего похожа на Basic:
signal on halt
do forever
... /* произвольный код */
end
halt: /* метка. далее обработчик ошибок halt, например просто выход */
exit
Это обработка завершения программы (кнопкой с терминала). Кроме halt, есть и другие «сигналы», в том числе, например, syntax (синтаксическая ошибка в интерпретируемом коде). Не помню, чтобы я это практически применял, но вполне логично проверять на синтаксическую правильность например то, что подается на вход interpret.
2. В нем не было регулярных выражений, как скажем в Perl (до Perl впрочем еще оставалось почти 10 лет). Сама концепция регулярных выражений появилась раньше, и они уже несомненно были, скажем, утилита grep уже существовала. Вместо них был оператор parse, который является некоторым упрощенным аналогом регулярок, с подстановкой групп в переменные.
parse [origin] [template]
Т.е. анализируем содержимое источника (который мог быть аргументом функции, переменной, выражением и т.п.) на соответствие шаблону. Шаблон мог содержать литералы, разделяющие значения, и переменные, которые получали значение из источника между этими разделителями. В каком-то смысле это похоже на группы в регулярных выражениях.
fio = 'Иван, Иванов'
parse var fio firstName ',' LastName
say firstName
say LastName
Это, конечно же, сильно проще регулярных выражений, хотя бы потому что, не было ни метасимволов типа \d, ни повторения в виде * или +, ни альтернатив, ничего такого.
3. У Rexx был API, который позволял его использовать как язык для скриптования не только ОС (CMS), но и любой другой программы, которая этот API поддерживала. Т.е. фактически, это был язык для написания DSL. Вероятно, один из первых. При этом среда исполнения могла реализовать и предоставить интерпретатору дополнительные функции или команды, доступные в итоге программе.
Выглядело это примерно так: при запуске текстового редактор XEDIT он искал где-то на доступных дисках файл под названием PROFILE XEDIT, который содержал скрипт на REXX. Этот скрипт мог назначать функциональные клавиши, и определять дополнительные команды, доступные из XEDIT. По смыслу сегодня это ближе всего к .bashrc, например. Или его аналогу для vim.
Соответственно, процедуры и функции REXX, определенные в PROFILE XEDIT, могли выполнять команды редактора, и получать доступ к информации об открытых файлах, в том числе и к содержимому самого файла.
4. Не требовалось какого-то специального синтаксиса для передачи команд хост-среде (т.е. ОС, или скажем текстовому редактору). Вместо этого, все выражения, которые Rexx не распознал, передавались на выполнение. Таким образом, чтобы записать внутри скрипта команду ОС, достаточно было заключить ее в кавычки, например:
'list * * * (stack'
Команду можно было построить путем конкатенации строковых констант и переменных. В качестве символа операции конкатенации выступал просто пробел.
Чтобы команду передать в другое приложение, имелся оператор address. Например, при использовании в среде текстового редактора XEDIT, чтобы выполнить команду не XEDIT, а ОС (CMS), нужно было сделать так:
address cms
'list * * * (stack'
5. В CMS был реализован API для доступа к стеку команд ОС, которым в том числе и Rexx пользовался для обмена данными. Команда выше возвращала полученный список файлов в стек, откуда скрипт мог их забрать, читая по строчке как стандартный ввод. В чем-то это аналогично юниксным пайпам, но несколько отличается от них. Пайп это все-таки поток байтов, а стек в CMS — это набор строк. В общем-то, это типичное различие между идеологией Unix и ОС от IBM, где файлы в значительной степени были ориентированы на строки фиксированной или переменной (но при этом известной) длины.
6. Как часть API приложения могли иметь доступ к переменным Rexx скрипта, могли читать и изменять их. Это был еще один способ обмена данными.
7. Была реализована довольно эффективная арифметика произвольной точности. Произвольной — это значит например 100 знаков и более — на сколько памяти хватит.
8. Управление памятью — автоматическое. Хотя и имелся например, оператор drop для удаления значения переменной (ассоциативного массива или его элементов), но в любом случае, дел с указателями и размерами блоков вы не имели.
9. В качестве единственной, но при этом весьма универсальной структуры данных были ассоциативные массивы, очень похожие например на javascript. То есть, если вы пишете a.1, это элемент массива a с ключом 1. Если пишете a.b, то это элемент с ключом b.
Вот скажем процедура добавления слова в словарь:
add_word: procedure expose dictionary.
parse arg w
n = dictionary.0 + 1
dictionary.n = w
dictionary.0 = n
return
Переменная dictionary в этом фрагменте — это наш ассоциативный массив. Процедура не возвращает этот массив, а делает его доступным извне (при помощи expose). dictionary.0 хранит число элементов в словаре (это просто соглашение, и как видите, мы его выполняем сами), dictionary.n — n-е слово словаря.
10. Средств низкоуровневого доступа к памяти или к API OC язык не имел, хотя в принципе написать для него что-то подобное было вполне возможно. Могу назвать пару примеров расширений языка, одно было предназначено для работы с базой данных SQL/DS (DB2), а второе — для обмена сообщениями между виртуальными машинами в рамках VM/SP. То есть, все что вы сможете оформить как DSL, в принципе может быть реализовано.
Поддержка REXX в приложениях
Кроме самой CMS сразу же поддерживал REXX основной текстовый редактор XEDIT. Он позволял писать макросы на REXX, которые можно было повесить на функциональные клавиши, или вызывать как команды, как из командной строки, так и в качестве т.н. «префиксных» команд, которые вводились рядом со строкой(строками) текста, и позволяли ей(ими) манипулировать. Например, команда d позволяла удалить строку, рядом с которой была введена, были команды копирования или перемещения, и другие. А макрос на REXX мог к примеру преобразовать строку в прописные или в строчные буквы.
Второе приложение, поддерживавшее язык на моей памяти, называлось DMS (Display Management System for CMS (DMS/CMS)), и было это средство разработки интерактивных программ, состоящих из экранных форм (panels). Из REXX можно было выбрать одну из заранее нарисованных форм, заполнить ее поля значениями, и показать на экране. После нажатия пользователем функциональной клавиши программа получала управление обратно, и переменные REXX содержали значения измененных полей. Самым близким функциональным аналогом DMS я бы назвал HTML и его формы. И по сложности (простоте) разработки это было пожалуй еще чуть проще, хотя по выразительным возможностям HTML разумеется выигрывает (например, в DMS вообще не было изображений, что впрочем объяснимо текстовыми терминалами).
Еще одно приложение, которое стоило бы упомянуть, называлось ISPF. Но это уже было сильно шире DMS, и было похоже скорее на интегрированную среду разработки, как она выглядит сейчас, с поправкой на текстовые терминалы. И да, текстовый редактор тоже программировался на REXX.
Начиная с VM/SP 6 в CMS появились средства для создания на экране терминала виртуальных окон переменного размера, и показа в них текста. Соответствующий API появился и для REXX (а точнее, это и было в первую очередь расширение REXX).
Еще одним весьма интересным применением REXX был так называемый автооператор. Это было приложение, запускавшееся в виртуальной машине оператора ОС, которое получало все сообщения, обычно приходившие на терминал оператора, и позволяло их обрабатывать программными средствами. Обработчик команд — скрипт на REXX. Таким образом можно было например дать некоторые возможности оператора многим людям, для чего эти люди просто посылали сообщения виртуальной машине оператора, а обработчик (от имени привилегированного пользователя — оператора) выполнял для них какие-то команды. Ну например, монтировал в системе тома дисков или лент.
Но такими простыми вещами возможности не ограничивались. Например, вы не можете послать сообщение пользователю, если его нет в системе. Посылаете его автооператору, он сохраняет его в очереди, а затем (получив сообщение, что пользователь вошел в систему) пересылает кому нужно. Фактически, это простая реализация электронной почты небольшим скриптом на REXX.
Влияние на разработку
Я бы сказал, что с появлением REXX в VM/SP 3 мы начали писать на нем значительную часть того, что ранее писалось например на PL/1. Это достаточно мощный, и в тоже время все еще простой язык, позволяющий нормально структурировать сравнительно большие приложения. В первую очередь наверное благодаря его возможностям интеграции, когда он применялся как DSL.
Возможности выполнять запросы к базе данных (SQL/DS), отображать экранные формы, читать и писать файлы покрывают значительную часть потребностей по разработке бизнес приложений.
Очевидно, язык многим понравился. Он был в OS/2, был на Амигах, под Windows, и много где еще. Позже были Object REXX с объектными расширениями, и NetREXX для JVM. Главное — этот язык все еще живой. И в общем-то, если бы пришлось выбирать, на чем сегодня писать скрипты, то рассматривая например Rexx, bash, совершенно птичий язык cmd.exe — я бы выбрал однозначно первый. Но вот в сравнении уже с языками более новыми, такими как Perl, Python, и многими-многими другими — уже все не так очевидно. Скажем, идея Rexx о передаче команд и переменных в среду исполнения была хороша — но идея, допустим COM, с его объектной ориентированностью — все же более функциональная.
Несмотря на прошедшие 40 лет, он все еще применяется в IBM z OS, да и книжка по языку, если верить википедии, вышла совсем недавно, примерно в 2012. Для разных ОС вполне можно загрузить живые реализации, и попробовать его самостоятельно.