Макроязык m4
Подготовка к выступлениям сразу на двух конференциях (SITOP и SofTool, если интересно) совершенно вывела из строя мой широковещательно-эпистолярный процессор. Не то чтобы мне не о чем написать (уж это-то всегда есть), просто темы все у меня какие-то трудоемкие, а кормить вас очередными горстями ссылок, не разбавляя более интеллектуальным контентом, рука не поднимается. Хотя и ссылки тоже будут, уж будьте уверены.
Но сейчас хочу поделиться впечатлениями от языка m4, который я внезапно выучил. Поскольку я тут понемногу готовлю мегапост (а скорее даже статью) про Autotools, то неизбежно заинтересовался, что это за странный язык m4, на котором написан Autoconf. Оказалось, на сайте GNU есть вполне приличный мануал по этому поводу. Есть даже чуть менее приличный мануал на русском.
История m4 уходит в глубину веков. Сначала появился макроязык GPM, аж в 1965 году. Потом Деннис Ричи, основательно перекроив GPM под нужды реального мира, написал m3. Затем к Ричи присоединился Керниган. Но ведь все знают, что когда Керниган и Ричи собираются вместе, обязательно выходит какая-нибудь нетленка. И вот эти два достойных мужа в едином порыве создают в 1977 году язык m4 (имя расшифровывается по аналогии с i18n и l10n). А уже в 1990 году появилась GNUтая версия. С тех пор мало что изменилось, разве что скорость выполнения немного подросла.
Мое знакомство с макроязыками ограничивалось до сих пор соответствующими средствами MASM на втором курсе института, да препроцессором языка C (который макроязыком может назвать только большой оптимист). К сишному препроцессору у меня была только одна претензия: на нем нельзя написать цикл. Представляете, как мне мало нужно было для счастья? Поэтому осилив за 5 часов чистого времени мануал по m4, я впал в состояние прострации и глубочайшего культурного шока. Язык не был похож ни на что известное мне до этого.
По мере чтения мануала я проходил следующие стадии:
- Ммм, интересно как!
- Сложно, блин. Черт, как же этот пример работает?..
- Определенно, язык писали идиоты для дебилов!
- Аааа, у меня в мозгу кавычки! Вытащите их!!!
- Понял! Да тут же все просто!
- Пожалуй, напишу на m4 генерацию документации для нашего проекта, а потом еще напишу парсер файлов определения локалей, а еще напишу…
На последней стадии нахожусь до сих пор. Рациональных объяснений этому не нахожу. Зато понял, что автор мануала не шутил, написав в самом начале:
Some people find
m4to be fairly addictive. They first usem4for simple problems, then take bigger and bigger challenges, learning how to write complex sets ofm4macros along the way. Once really addicted, users pursue writing of sophisticatedm4applications even to solve simple problems, devoting more time debugging theirm4scripts than doing real work. Beware thatm4may be dangerous for the health of compulsive programmers.
В чем принципиальное отличие m4 от других языков? Есть языки императивные и декларативные, есть структурные, функциональные и объектно-ориентированные. Все они в конечном итоге нужны для построения программы, которая выполняет определенные действия. Еще есть языки текстовых фильтров: sed, awk и т.п. Они преобразуют входные данные в соответствии с заданными правилами. Язык m4 — порождающий. Программа, написанная на нем, не выполняется — она разворачивается в текст. Поначалу это несколько выносит мозг, но потом начинаешь ценить простоту и красоту подобных конструкций:
define(`quote', `ifelse(`$#', `0', `', ``$*'')')dnl
define(`foreach',
`_args(`$0', `$#', 3)
foreach1($1, $2, $3)'
)
define(`arg1', `$1')
define(`foreach1',
`ifelse(
quote($3), `',
`',
`define(`$1', `arg1($3)')$2`'$0(`$1', `$2', shift(shift(shift($@))))'
)'
)Ага, это именно то, о чем вы подумали. Цикл foreach, построенный на примитивах define, ifelse и рекурсии. Почти как в Lisp.
Да, синтаксис не блещет изяществом. Кроме того, программа не делится на строки; конец строки воспринимается буквально и попадает в порожденный текст (кроме определенных случаев). Но зато язык в действительности очень простой. В нем очень мало встроенных средств, дай бог наберется три десятка встроенных макросов. Но, как и любой язык с малым количеством базисных элементов, m4 требует определенной умственной акробатики при написании сложных конструкций. А свободное восприятие открывающих и закрывающих кавычек и нумерованных переменных a la Perl/bash/PHP — вопрос пары дней практики.
m4 на удивление непопулярен. В крупных проектах, за исключением Autoconf, замечен не был (UPD: еще sendmail, спасибо Юрию). Может быть, есть какие-то другие порождающие языки, намного красивее и удобнее? Пока же я вижу, что m4 очень простое и мощное средство, позволяющее сэкономить массу сил (масса сил… хм). Еще одно преимущество — m4 есть везде, где есть Autoconf, а это практически все *nix-системы.
Если интересно, могу развить тему и написать небольшое руководство.





