Lua-фильтры в Pandoc
Pandoc поддерживает большое количество форматов разметки, как для входных данных, так и для выхода. Конвертация основана на том, что входные данные преобразуются в определённое внутреннее представление, а результат получается переводом из внутреннего представления в выходной формат.
Внутренний формат — абстрактное синтаксическое дерево. Чтобы увидеть его в виде JSON используете следующую команду:
pandoc -o book.json book.md
Pandoc позволяет внести изменения в AST с помощью фильтров. Таким способом можно добиться поддержки своего формата, такого как Comtext. Фильтры можно писать на разных языка программирования, однако, фильтры на Lua выделяются тем, что не требуют дополнительных программ, так как интерпретатор Lua встроен в Pandoc.
Следующий фильтр удаляет из AST разметку номеров страниц в формате Comtext.
local text = pandoc.text
local function is_page_number(page_number_start, space, page_number_end)
return page_number_start and page_number_start.t == 'Str'
and page_number_end and page_number_end.t == 'Str'
and space and space.t == 'Space'
and page_number_start.text == '[#'
and page_number_end.text:sub(-1) == ']'
end
local function is_inline_page_number(page_number_start, space, page_number_end)
return page_number_start and page_number_start.t == 'Str'
and page_number_end and page_number_end.t == 'Str'
and space and space.t == 'Space'
and page_number_start.text:sub(-2) == '[#'
--and page_number_end.text:sub(-1) == ']'
end
function Inlines (inlines)
-- Go from end to start to avoid problems with shifting indices.
for i = #inlines-2, 1, -1 do
if is_page_number(inlines[i], inlines[i+1], inlines[i+2]) then
inlines:remove(i+2)
inlines:remove(i+1)
inlines:remove(i)
elseif is_inline_page_number(inlines[i], inlines[i+1], inlines[i+2]) then
inlines[i].text = inlines[i].text:sub(1, -3)
inlines[i+2].text = inlines[i+2].text:gsub('(.-])', '')
inlines:remove(i+1)
end
end
return inlines
end
В Lua-фильтрах каждая глобальная функция сравнивается с названием названиями типов элементов синтаксического дерева. Если есть совпадающие то они вызываются для всех элементов этого типа. Если функция возвращает nil
, то элемент остаётся без изменения. Если функция возвращает новый элемент, то этот элемент обновляется в исходном дереве.
В примере выше, функция Inlines
вызывается для всех абзацев и содержит в себе коллекцию со словами внутри абзаца. Во время последовательного перебора элементов происходит поиск тега [# номер_страницы]
и если такой обнаруживается, то эти элементы удаляются. Особый случай представляет собой разрыв страницы на середине слова.