Искусство программирования для Unix
Шрифт:
Макропроцессор Unix, т4 представляет собой другой очень простой декларативный мини-язык (т.е. язык, в котором программа выражается как набор желаемых связей или ограничений, а не как явные действия). Он часто используется в качестве препроцессора для других мини-языков.
Рис. 8.1. Классификация языков
make-файлы Unix, предназначенные для автоматизации процесса сборки, выражают зависимости между исходными и производными файлами57,
Язык XSLT, который используется для описания трансформаций XML-файлов, соответствует верхнему уровню сложности декларативных мини-языков. Он довольно сложен для того, чтобы рассматривать его как мини-язык, однако разделяет некоторые важные характеристики таких языков, которые подробнее рассматриваются ниже при изучении XSLT.
Спектр мини-языков простирается от декларативных (с неявными действиями) к императивным (с явными действиями). Синтаксис файла конфигурации программы fetchmail(l) можно рассматривать либо как очень слабый императивный язык, либо как декларативный язык с неявной управляющей логикой. Языки обработки текстов troff и PostScript являются императивными языками с большим количеством встроенной специальной информации о прикладной области.
Некоторые императивные мини-языки для решения специальных задач граничат с универсальными интерпретаторами. Они достигают данного уровня, когда явно являются языками Тьюринга, т.е. они могут выполнять условные операции и циклы
(или рекурсию)58 с функциями, которые предназначены для использования в качестве управляющих структур. В отличие от них, некоторые языки только отчасти являются языками Тьюринга. В них имеются функции, которые можно использовать для реализации управляющих структур как побочный эффект того, для чего они фактически предназначены.
Интерпретаторы Ьс(1) и dc(1), рассмотренные в главе 7, являются хорошими примерами специализированных императивных мини-языков, которые явно являются языками Тьюринга.
Такие языки, как Emacs Lisp и JavaScript, находятся в области универсальных интерпретаторов. Языки Emacs Lisp и JavaScript предназначены для использования в качестве полных языков программирования, работающих в специализированных средах. Более подробно они описываются ниже при рассмотрении встроенных языков сценариев.
Область интерпретаторов представляет собой область возрастающей неопределенности. Оборотной стороной этого является то, что более универсальный интерпретатор включает в себя меньше предположений о среде, в которой он работает. С возрастающей неопределенностью обычно приходит более развитая онтология типов данных. Shell и Tel обладают
8.2. Применение мини-языков
Разработка программ с помощью мини-языков затрагивает две отдельные проблемы. Одна из них заключается в том, чтобы уметь пользоваться имеющимися в инструментарии мини-языками и понимать, когда их можно применять такими, как они есть. Другая проблема — знать, когда целесообразно разрабатывать для приложения нестандартный мини-язык. Для того чтобы помочь читателю развить оба аспекта конструкторского мышления, почти половина данной главы состоит из учебных примеров.
8.2.1. Учебный пример: sng
В главе 6 рассматривалась утилита sng(1), преобразовывающая PNG-файл в редактируемую полностью текстовую форму. Формат файлов данных SNG заслуживает повторного рассмотрения здесь для контраста, поскольку он не вполне является узкоспециальным мини-языком. Он описывает расположение данных, но не связывает с ними какую-либо предполагаемую последовательность действий.
Однако SNG действительно имеет одну общую важную характеристику с узкоспециальными мини-языками, которую не поддерживают структурированные двоичные форматы данных, подобные PNG, — прозрачность. Структурированные файлы данных позволяют без использования мини-языка взаимодействовать средствам редактирования, преобразования и создания, которые не имеют информации о конструкторских "предположениях" друг друга. В случае SNG добавляется то, что данный формат как узкоспециальный мини-язык, предназначен для простого просмотра и редактирования с помощью универсальных средств.
8.2.2. Учебный пример: регулярные выражения
Одним из видов спецификации, который периодически появляется в инструментах Unix и языках сценариев, является регулярное выражение (regular expression, или regexp для краткости). Здесь регулярные выражения рассматриваются как декларативный мини-язык для описания текстовых шаблонов. Часто регулярные выражения встраиваются в другие мини-языки. Регулярные выражения настолько распространены, что их едва ли можно считать мини-языком, однако они заменяют то, что в противном случае представляло было собой огромные объемы кода, реализующего различные (и несовместимые) возможности поиска.
В данном введении не рассматриваются такие подробности, как POSIX-расширения, обратные ссылки и особенности интернационализации. Более подробное изложение способа их применения представлено в книге "Mastering Regular Expressions" [22].
Регулярные выражения описывают шаблоны, которые могут либо совпадать, либо не совпадать со строками. Простейшим средством для работы с регулярными выражениями является утилита grep(1), фильтр, который переправляет со стандартного ввода на стандартный вывод каждую строку, соответствующую указанному регулярному выражению. Форма записи регулярных выражений кратко представлена в таблице 8.1.