июня 15, 2011

m4: временные макросы

Часто при использовании m4 может возникнуть необходимость использования локальных определений. Например, в случае языка разметки часто нужно оформлять какие-то списки. Списки бывают разных типов — по оформлению: нумерованный, с кружочками; по применению: оглавление, список литературы. И хочется иметь возможность быстро сменять один тип на другой, т.е. было бы удобно записывать как-то так:

LIST(
  ITEM(Ананас)
  ITEM(Банан)
  ITEM(Кокос))
REFERENCES(
  ITEM(Энциклопедический словарь Брокгауза и Ефрона: В 86 томах (82 т. и 4 доп.). — СПб.: 1890—1907.)
  ITEM(Перевод главы о бананах из книги Julia F. Morton «Fruits of Warm Climates» 1987)
  ITEM(Кокосовая пальма — статья из Большой советской энциклопедии))


Здесь в обоих конструкциях используется макрос ITEM, но в разных смыслах: в первом случае он будет раскрыт в нумерованный элемент, во втором — в элемент списка литературы.

Однако, подобный синтаксис реализовать в m4 невозможно. В любых конструкциях сперва вычисляются аргументы (т.е. ITEM'ы), и только после этого вызываются макросы, в которые эти аргументы передаются, т.е. в LIST и REFERENCES. Поэтому LIST и REFERENCES не могут никак повлиять на то, как будут вычисляться ITEM'ы. Как можно было бы обойти это ограничение? Самый простой способ — отказаться от локальных объявлений, а сделать глобальные, но с разными именами:
LIST(
  ITEM(Ананас)
  ITEM(Банан)
  ITEM(Кокос))
REFERENCES(
  RITEM(Энциклопедический словарь Брокгауза и Ефрона: В 86 томах (82 т. и 4 доп.). — СПб.: 1890—1907.)
  RITEM(Перевод главы о бананах из книги Julia F. Morton «Fruits of Warm Climates» 1987)
  RITEM(Кокосовая пальма — статья из Большой советской энциклопедии))


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

LIST
  ITEM(Ананас)
  ITEM(Банан)
  ITEM(Кокос)
_LIST
REFERENCES
  ITEM(Энциклопедический словарь Брокгауза и Ефрона: В 86 томах (82 т. и 4 доп.). — СПб.: 1890—1907.)
  ITEM(Перевод главы о бананах из книги Julia F. Morton «Fruits of Warm Climates» 1987)
  ITEM(Кокосовая пальма — статья из Большой советской энциклопедии)
_REFERENCES


Теперь уже можно внутри REFERENCES ввести своё собственное объявление ITEM'а, а внутри _REFERENCES восстановить старое.

Создание временных макросов в m4 имеет несколько тонкостей. Во-первых, вместо обычного define следует использовать пару pushdef и popdef. Первый макрос определяет новый макрос, сохраняя все предыдущие, popdef удаляет последнее определение, восстанавливая предыдущие.

Во-вторых связано с использованием локальных переменных: если записать
define(`LIST', `<OL>pushdef(`ITEM', `<LI>$*</LI>')')
то вместо $* будет подставлен параметр макроса LIST, а не макроса ITEM. Более того, даже если заключить параметр в сколь угодно большое кол-во символов цитирования, всё равно оно будет считаться параметром макроса LIST. Я придумал решение, быть может не очень красивое, но рабочее: нужно сгенерировать текст «$*» вычислением, самый простой способ — конкатенация:
define(`CONCAT', `$1$2')
define(`_DS', `CONCAT($,*)') 
define(`LIST', `<OL>pushdef(`ITEM', `<LI>_DS</LI>')')
define(`_LIST', `popdef(`ITEM')</OL>')

(DS — аббревиатура для Dollar Star).

Можно аналогичным образом определить макросы _D1, _D2, ..., _D9 для, соответственно, $1, $2, ..., $9.

Ещё есть красивое решение, которое заключается в написании макросов _S, _1, _2, ..., _9, которые заменяются на, соответственно, *, 1, 2, ..., 9. Тогда «локальные параметры» подмакросов можно будет записывать как $_S, $_1, $_2, ..., $_9.

Составные конструкции можно ещё усовершенствовать: вместо пар LIST/_LIST, REFERENCES/_REFERENCES можно задавать всего по одному макросу LIST и REFERENCES, внутри которых будет определяться макрос END, определённый аналогичным ITEM'у способом. Этот способ реализован в моём языке разметок, в файлах util.m4 и html.m4 (см. списки и таблицы). В файле examples.m4 можно посмотреть примеры использования всего этого, а в файле examples.html то, что из этого сгенерировано: как видно, все списки и таблицы благополучно раскрываются в то, что нужно.


Комментариев нет:

Отправить комментарий

Постоянные читатели

Обо мне

Моя фотография
Мой e-mail: vitek_03(at)mail(dot)ru