Pandoc Lua 过滤器:免外部工具,高效处理文档转换!
简介
长期以来,Pandoc 支持过滤器,可在解析和写入阶段间操作 Pandoc 抽象语法树(AST)。[传统的 Pandoc 过滤器](https://pandoc.org/filters.html)接受 Pandoc AST 的 JSON 表示,生成修改后的 AST 的 JSON 表示,能用任何编程语言编写,通过 `--filter` 选项从 Pandoc 调用。不过,传统过滤器有缺点。一是将 JSON 写入标准输出并从标准输入读取有开销;二是其能否正常工作取决于用户环境细节,可能需特定编程语言解释器和操作 Pandoc AST 的 JSON 库,无法简单提供给有特定版本 Pandoc 可执行文件的人使用。从 2.0 版本起,Pandoc 支持用 Lua 编写过滤器,无需依赖外部工具。Pandoc 可执行文件内置 Lua 解释器(版本 5.4)和创建 Pandoc 过滤器的 Lua 库,Pandoc 数据类型直接转换为 Lua,避免了 JSON 读写开销。
Lua 过滤器结构
Lua 过滤器是表格,键为元素名称,值为作用于这些元素的函数。过滤器通常存于单独文件,通过 `--lua-filter` 命令行参数传递。`--lua-filter` 选项可多次使用,Pandoc 按命令行顺序应用所有过滤器。Pandoc 期望每个 Lua 文件返回一个过滤器。若过滤器脚本无返回值,Pandoc 会收集所有名称与 Pandoc 元素对应的顶级函数生成单一过滤器。目前,也可从 Lua 文件返回过滤器列表,按顺序调用,但现在不建议,推荐用 `walk` 方法,此功能可能会移除。对于每个过滤器,Pandoc 遍历文档,将每个元素应用于该过滤器。若过滤器中有与元素对应的条目,元素会传递给 Lua 元素过滤函数。过滤函数返回值必须是 `nil`、一个 Pandoc 对象或一个 Pandoc 对象列表。函数输出必须与输入类型相同。若没有与元素节点类型匹配的函数,过滤系统将查找更通用的回退函数。没有匹配函数的元素保持不变。
元素序列过滤器
某些过滤任务需了解元素在文档中出现的顺序,仅检查单个元素不够。有 `Inlines (inlines)` 和 `Blocks (blocks)` 两个特殊函数名可定义对块列表或内联列表的过滤器。这些过滤函数结果必须是 `nil` 或与输入参数类型相同的列表。此功能在 Pandoc 2.9.2 中添加。
遍历顺序
可通过将键 `traverse` 设置为 `'topdown'` 或 `'typewise'` 选择过滤器的遍历顺序,默认值是 `'typewise'`。Pandoc 2.17 中添加对此的支持,早期版本会忽略 `traverse` 设置。按类型遍历,过滤器集中的元素过滤函数按固定顺序调用,仍可手动使用 `walk` 方法运行过滤器来改变顺序。自上而下遍历,从根节点到叶子节点深度优先遍历文档树,可从过滤函数返回 `false` 作为第二个值提前终止。
全局变量
Pandoc 通过设置全局变量将额外数据传递给 Lua 过滤器,包括 `FORMAT`、`PANDOC_READER_OPTIONS`、`PANDOC_WRITER_OPTIONS`、`PANDOC_VERSION`、`PANDOC_API_VERSION`、`PANDOC_SCRIPT_FILE`、`PANDOC_STATE`、`pandoc`、`lpeg`、`re`。
Pandoc 模块
`pandoc` Lua 模块加载到过滤器的 Lua 环境中,提供元素创建函数和对 Pandoc 一些主要功能的访问。
Lua 解释器初始化
可通过在 Pandoc 的数据目录中放置 `init.lua` 文件控制 Pandoc 的 Lua 解释器的初始化。
调试 Lua 过滤器
通过静态分析可避免许多错误,可使用 [`luacheck`](https://github.com/lunarmodules/luacheck) 进行。William Lupton 编写了一个 Lua 模块,包含调试 Lua 过滤器的实用函数。可使用调试接口暂停执行,在 Pandoc 内部逐行调试 Lua 过滤器。
常见陷阱
包括 AST 元素未更新、模式行为依赖于区域设置、字符串库不支持 Unicode。
示例
介绍了宏替换、在 LaTeX 和 HTML 输出中居中显示图像、在元数据中设置日期、删除引用前的空格、用元数据值替换占位符、为手册页修改 Pandoc 的 `MANUAL.txt`、从论文创建讲义、统计文档中的单词数、创建表格、从文档中提取链接、将 ABC 代码转换为音乐符号、使用 Ti _k_ Z 构建图像等过滤器示例。
Lua 类型参考
本节描述了 Lua 过滤器可用的对象类型。有关创建这些对象的函数,请参阅 `pandoc` 模块。