языка; использование более выразительных языков означает более короткие программы и меньшее количество ошибок.

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

Ошибочный путь к конструкции мини-языка — это растягивать путь к нему, постепенно добавляя заплатки и сложные функции. На этом пути файл спецификации содержит задатки более скрытой управляющей логики и более замысловатых специализированных структур до тех пор, пока незаметно не станет сложным уникальным языком. Несколько 'легендарных кошмаров встают' на этом пути. Каждый Unix-гуру вздрогнет при упоминании конфигурационного файла sendmail.cf, связанного с почтовым транспортом sendmail.

К сожалению, большинство разработчиков создают свой первый мини-язык ошибочным способом и только позднее осознают, насколько он запутан. Как очистить мини-язык? Иногда ответ предполагает переосмысление конструкции всего приложения. Другим печально известным примером был редактор TECO, в котором возник первый макрос, а затем появились циклы и условные операторы по мере того, как программисты хотели использовать его для упаковки редактирующих подпрограмм с возрастающей сложностью. Созданная в результате уродливая конструкция была в конечном итоге исправлена путем переработки всего редактора, основанного на заранее продуманном языке. Так развивался Emacs Lisp (который рассматривается ниже).

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

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

Начать следует с небольшой классификации, которая поможет лучше понять дальнейший материал.

8.1. Классификация языков

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

В главе 5 рассматривались Unix-соглашения для файлов данных. В них имеется определенный спектр сложности. На самом низком уровне находятся файлы, в которых создаются простые ассоциации между именами и свойствами, хорошими примерами таких форматов являются файлы /etc/passwd и .newsrc. Далее представлены форматы, которые осуществляют маршалинг или сериализацию структур данных. Одинаково хорошими примерами в данном случае являются форматы PNG и SNG.

Структурированные форматы файлов данных начинаются на границе мини-языков, когда они выражают не только структуру, но и действия, выполняемые в некоторой интерпретирующей среде (т.е. памяти за пределами самого файла данных). XML-разметка стремится 'перешагнуть' эту границу. Примером такого мини-языка, представленным в данной главе, является Glade, генератор кода для создания GUI-интерфейсов. Форматы, которые одновременно разработаны для чтения и записи человеком (скорее человеком, чем программами) и используются для генерации кода, прочно укрепились в области мини-языков. Классическими примерами являются утилиты yacc и lex. Программы glade, yacc и lex описываются в главе 9.

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

Рис. 8.1. Классификация языков

make-файлы Unix, предназначенные для автоматизации процесса сборки, выражают зависимости между исходными и производными файлами[77], а также команды, необходимые для создания каждого производного файла из его исходного кода. При выполнении команда make использует данные объявления для обхода предполагаемого дерева зависимостей, выполняя наименьшую необходимую работу для обновления сборки. Подобно спецификациям yacc и lex, make-файлы являются декларативным мини-языком. Они устанавливают ограничения, которые предполагают действия, выполняемые в интерпретирующей среде (в данном случае в той части файловой системы, где расположены исходные и сгенерированные файлы), make-файлы дополнительно рассматриваются в главе 15.

Язык XSLT, который используется для описания трансформаций XML-файлов, соответствует верхнему уровню сложности декларативных мини-языков. Он довольно сложен для того, чтобы рассматривать его как мини-язык, однако разделяет некоторые важные характеристики таких языков, которые подробнее рассматриваются ниже при изучении XSLT.

Спектр мини-языков простирается от декларативных (с неявными действиями) к императивным (с явными действиями). Синтаксис файла конфигурации программы fetchmail(1) можно рассматривать либо как очень слабый императивный язык, либо как декларативный язык с неявной управляющей логикой. Языки обработки текстов troff и PostScript являются императивными языками с большим количеством встроенной специальной информации о прикладной области.

Некоторые императивные мини-языки для решения специальных задач граничат с универсальными интерпретаторами. Они достигают данного уровня, когда явно являются языками Тьюринга, т.е. они могут выполнять условные операции и циклы (или рекурсию)[78] с функциями, которые предназначены для использования в качестве управляющих структур. В отличие от них, некоторые языки только отчасти являются языками Тьюринга. В них имеются функции, которые можно использовать для реализации управляющих структур как побочный эффект того, для чего они фактически предназначены.

Интерпретаторы bc(1) и dc(1), рассмотренные в главе 7, являются хорошими примерами специализированных императивных мини-языков, которые явно являются языками Тьюринга.

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

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

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

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

Отметить Добавить цитату