июня 02, 2011

m4: Универсальный язык разметок

Этот пост — небольшая обучалка по m4. Как мне кажется, приведённый ниже пример
хорошо иллюстрирует некоторые базовые принципы использования m4.

Я весьма часто пишу тексты, и для разметки приходится использовать довольно разный
синтаксис: то html, то LaTex, то google wiki, то bbCode (upd ещё vkontakte разметку бывает
приходится использовать). Даже html бывает разного вида (например, некоторые блоги
сами вставляют тег br в конце каждой строки).

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

Для обеспечения единого синтаксиса я решил воспользоваться m4, а именно:
текст будет писаться с использованием макросов, а в зависимости от того
какой выходной файл нужно будет получить, эти макросы будут по разному
раскрываться.

Рассмотрим это на примере HTML и LaTeX. Я создал два файла: html.m4 и tex.m4.
Когда требуется из файла изготовить текст на HTML, то следует вызывать
m4 html.m4 file.m4 > file.html
Если же требуется получить LaTeX, то нужно вызывать
m4 tex.m4 file.m4 > file.tex

Этот вызов сначала выполнит html.m4 или tex.m4, потом file.m4, сольёт результат вместе и
запишет в file.out. При этом в файлах html.m4 и tex.m4 находится исключительно служебная
информация, и мы не хотим, чтобы из них что-то печаталось. Поэтому в начало
каждого такого файла добавляется строка
divert(-1)
которая весь дальнейший вывод выбрасывает, а в конце файла пишем
divert
после чего вывод будет происходить нормально.

Приступим, наконец, к написанию макросов. Самое простое — перевод строки.
html.m4:
define(`BR', `<BR>')
tex.m4:
define(`BR',`\\')

Я решил, что все макросы будут писаться большими буквами, чтобы было по-меньше
конфликтов имён.

Перейдём к более сложному макросу: жирный шрифт. Использование такого макроса
будет выглядеть примерно так: BOLD(жирный шрифт). Для реализации нужно
в теле макроса написать $* — вместо него будет подставлено всё, что находится
между скобочек:
html.m4:
define(`BOLD', `<strong>$*</strong>')
tex.m4:
define(`BOLD', `\textbf{$*}')

Далее, запись списков. Я выбрал такой синтаксис:
LIST(
  ITEM(Ананас)
  ITEM(Манго)
  ITEM(Кокос)
)


Оно создаст соответствующий нумерованный список. Реализация:
html.m4:
define(`LIST',`<OL>$*</OL>')
define(`ITEM',`<LI>$*</LI>')

tex.m4:
define('LIST', `\begin{enumerate}$*\end{enumerate}')
define('ITEM', `\item $*')


Перейдём к более сложному примеру: ссылки. Ссылки уже требуют двух параметров:
куда ссылается и что отображается. Для получения отдельных параметров вместо $*
следует использовать $1, $2, и т.д. Поэтому реализацию ссылки можно бы было
записать так:
define(`LINK', `<a href="$1">$2</a>')

Но эта конструкция имеет проблему: если написать
LINK(google.com, Используй гугл перед тем, как задать вопрос, пожалуйста)
то m4 будет думать, что тут четыре параметра, и второй — «Используй гугл перед тем»,
а не вся фраза целиком. Для решения проблемы следует использовать макрос «shift»:
он возвращает все свои параметры, кроме первого.
html.m4
define(`LINK', `<a href="$1">shift($*)</a>')
tex.m4
define(`LINK', `\href{shift($*)}{$1}')

Теперь указанный выше пример сработает корректно.

Далее — дело техники, уже перечисленного достаточно для реализации многого.
В случае, если при наборе какого-то текста критично то, что макросы для разметки слишком длинные, то легко можно вставить где-то в тексте строки вида:
define(`B',`BOLD')
и использовать краткие варианты макросов, ничего при этом не потеряв.

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


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

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

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

Обо мне

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