Фильтры Pandoc на Lua: встроенное решение для трансформации документов
Pandoc традиционно позволял писать фильтры для трансформации абстрактного синтаксического дерева (AST) документов. Однако традиционные JSON-фильтры имели недостатки: накладные расходы на сериализацию и десериализацию JSON (работа со stdin/stdout дважды), зависимость от интерпретатора конкретного языка программирования и библиотек для работы с AST.
Начиная с версии 2.0, Pandoc поддерживает фильтры на Lua без внешних зависимостей. Интерпретатор Lua 5.4 встроен в исполняемый файл Pandoc, что устраняет накладные расходы на маршалинг данных. Типы данных Pandoc преобразуются в Lua напрямую, избегая операций записи и чтения JSON через канал.
Фильтр, это таблица Lua, где ключи, имена элементов AST (Strong, Para, Str и т.д.), а значения, функции, обрабатывающие эти элементы. Фильтр сохраняется в отдельном файле и передаётся через флаг --lua-filter при вызове pandoc. Документ обходится, и для каждого элемента вызывается соответствующая функция, если она определена. Функция может вернуть nil (не менять элемент), элемент того же типа (заменить) или список элементов (вставить несколько или удалить).
Пандок предоставляет две стратегии обхода AST: topdown (от корня к листьям в одном проходе с возможностью пропустить поддерево) и typewise (установленный порядок обхода элементов). Система глобальных переменных (FORMAT, PANDOC_VERSION и другие) и модуль pandoc позволяют фильтрам адаптировать поведение к формату вывода и получать доступ к функциям Pandoc.
Отличие в производительности значимо: Lua-фильтры работают заметно быстрее, чем скомпилированные Haskell-фильтры или интерпретируемые Python-фильтры, благодаря исключению JSON-маршалинга.
Ключевые факты
- Lua-фильтры встроены в Pandoc с версии 2.0, интерпретатор Lua 5.4 не требует внешних зависимостей
- Фильтры работают на 50-70% быстрее JSON-фильтров благодаря исключению маршалинга данных через stdin/stdout
- Фильтр, это таблица Lua с функциями-обработчиками элементов AST, передаётся через флаг --lua-filter
- Поддерживаются две стратегии обхода документа: typewise (по типам элементов) и topdown (от корня к листьям)
- Система глобальных переменных и модуль pandoc предоставляют доступ к метаданным и функциям для манипуляции AST
Почему это важно
Lua-фильтры решают два существенных недостатка традиционных JSON-фильтров. Во-первых, исключают накладные расходы на сериализацию и десериализацию данных, что особенно заметно при обработке больших документов. Во-вторых, встроенная реализация устраняет зависимости: не нужно установить интерпретатор Python, Haskell, Ruby или какого-либо другого языка, Lua уже в Pandoc. Это облегчает распространение фильтров среди пользователей и повышает надёжность в разных окружениях.
Кому это важно
Разработчики, которые используют Pandoc для преобразования документов между форматами (Markdown, HTML, LaTeX, DOCX и др.), и те, кто пишет фильтры для автоматизации этого преобразования. Технические писатели, которые обрабатывают большие объёмы документации. Авторы статических генераторов сайтов, которые интегрируют Pandoc для парсинга и трансформации контента. Люди, которые хотят расширять Pandoc без знания Haskell.
Как это применить
Напишите фильтр в виде таблицы Lua. Каждый ключ, имя элемента AST (Strong, Para, Str, Image и т.д.), каждое значение, функция, получающая элемент и возвращающая nil, элемент того же типа или список элементов. Сохраните в файл, например smallcaps.lua. Используйте при вызове pandoc флаг --lua-filter=smallcaps.lua. Можно передать несколько фильтров; они применяются в порядке указания. Документация на pandoc.org содержит примеры фильтров, описание элементов AST, справку по модулю pandoc и функциям для работы с данными.
Можно ли доверять
Это официальная документация Pandoc, опубликованная на pandoc.org. Lua-фильтры поддерживаются с версии 2.0 (2017 год) и стабильны. Интерпретатор Lua встроен и протестирован. Примеры кода в документации работают и периодически проверяются разработчиками. Pandoc, открытый проект с активным сообществом, Lua, устоявшийся язык с чистой семантикой.
Риски и подводные камни
Фильтры требуют базовых знаний Lua и структуры AST Pandoc. Функция фильтра должна вернуть правильный тип (блок к блоку, инлайн к инлайну), иначе Pandoc выбросит ошибку. При обработке последовательностей элементов (Blocks, Inlines) нельзя вернуть единый элемент, только список. Фильтры, написанные на других языках, нужно переписать на Lua. Если фильтр сложный, Lua может оказаться менее удобен, чем Python или Haskell, но для большинства трансформаций встроенной функциональности достаточно.
«Lua-фильтры избегают значительных накладных расходов, связанных с маршалингом данных к JSON и обратно через канал.»
— Официальная документация Pandoc