В предыдущей статье был представлен фильтр для pandoc, который позволяет настраивать стили изображений. В дальнейшем возможности фильтра были расширены и новая версия размещена на github. Основное нововведение — это возможность ссылаться на определение стиля, объявленного в метаданных документа, прямо из ссылки на изображение в исходном документе. Например, ссылка
![$img_style$ *Пример изображения*](../images/gnome-terminal-italic.png)
без использования фильтра styleFromMeta будет оформлена с альтернативным текстом img_style Пример изображения, причем запись img_style будет рассматриваться как встроенная математическая запись (inline math), поскольку она окружена символами доллара. Фильтр styleFromMeta проверяет, не начинается ли содержимое внутри квадратных скобок в определениях ссылок (и не только на изображения) со встроенной математической записи, и если это так, то пытается найти объект с именем этой записи в метаданных документа. В случае удачного поиска найденный объект подставляется в выходной документ с возможной заменой плейсхолдеров $ALT$, $SRC$ и $TITLE$ на значения из определения ссылки. Вместо $ALT$ подставляется остаток содержимого внутри квадратных скобок определения ссылки без начальных пробелов. В нашем примере значению $ALT$ будет соответствовать строка Пример изображения.
Алгоритм поиска ссылок с возможным указанием на требуемый стиль реализуется с помощью сопоставления с образцом (pattern matching). Например, так.
substStyle :: Format -> MMap -> Block -> Block
substStyle (Format fm) m
b@(Para [Image ((Math InlineMath style):alt) (src, title)]) =
Я не привел тело функции, поскольку к обсуждаемой проблеме оно не имеет отношения. Тип MMap объявлен как
type MMap = M.Map String MetaValue
, он соответствует способу хранения метаданных документа в pandoc.
Главная проблема определения substStyle состоит в необходимости записывать образец для поиска стиля как Math InlineMath style
. Но ведь это же просто деталь реализации! Просто так совпало, что мы выбрали обрамление долларами для отметки ссылок на стиль, и ссылки эти не имеют никакого отношения к встроенным математическим записям. Иными словами, использование оригинальных имен конструкторов в данном случае искажает семантику образца и не отражает наше намерение использовать его для извлечения ссылок на стиль. Определение
substStyle (Format fm) m b@(Para [Image ((Style style):alt) (src, title)]) =
выглядело бы гораздо лучше.
Провернуть такой трюк позволит новое синтаксическое расширение Синонимы образцов (pattern synonyms), появившееся в ghc-7.8. Выражение Style в новом определении substStyle можно объявить как синоним образца.
pattern Style x <- Math InlineMath x
В данном случае мы объявили односторонний (unidirectional) синоним образца: для определения таких синонимов используется символ <-
. Двухсторонние (bidirectional) синонимы объявляются с помощью символа =
. Разница между двумя типами синонимов образцов простая: односторонние синонимы можно использовать только для сопоставления с образцом, в то время как двухсторонние — как обычные выражения. Подробнее о синонимах образцов можно узнать здесь.
Все это хорошо, но что делать, если у нас в наличии старый компилятор ghc? Он не поймет, что это за объявление pattern Style x
и что за Style
объявлен в определении функции styleFromMeta. В нашем случае проблема легко решается введением условной компиляции и применением макросов cpp. В самом начале программы добавим объявления
{-# LANGUAGE CPP #-}
#if __GLASGOW_HASKELL__ >= 708
{-# LANGUAGE PatternSynonyms #-}
#endif
В первой строке заявлено, что мы хотим использовать препроцессор cpp. Далее, если версия ghc не древнее 7.8, мы включаем расширение PatternSynonyms. После объявлений импортов модулей вставляем строки
#if __GLASGOW_HASKELL__ >= 708
pattern Style x <- Math InlineMath x
#else
#define Style Math InlineMath
#endif
Если компилятор ghc древнее версии 7.8, то препроцессор cpp заменит слово Style на два слова Math InlineMath во всем исходном коде программы непосредственно перед ее компиляцией.
Комментариев нет:
Отправить комментарий