解决“Pandoc Filters”页面上的第一个练习

https://pandoc.org/filters.html#exercises 上的第一个问题要求将所有文本转换为大写,除非它是链接或URL的一部分。所以,我在 https://pandoc.org/lua-filters.html#execution-order上阅读了有关lua过滤器中“Execution Order”的讨论,并提出了以下内容:

text = require 'text'

links = {}

function Link(el)
  links[el.target] = el.content
  return el
end

function Str(el)
  el.text = text.upper(el.text)
  return el
end

function Inlines(elems)
  for i=1,#elems,1 do
    if elems[i].tag == 'Link' then
      elems[i].content = '<====' .. links[elems[i].target] .. '====>' -- 只是为了我能看到它在文档中。
      -- elems[i].content = pandoc.Str 'hello'
    end
  end
  return elems
end

--[[ -- 显式地强制过滤器顺序 -- 来自“Execution Order”列表...
return {
  { Link = Link,
    Str = Str,
    Inlines = Inlines
  }
}
]]

认为这会解决我的问题。但是不知道我做错了什么,无法使其起作用。我还尝试通过强制调用过滤器的顺序来显式排列表格并进行了调试,但似乎仍然无法解决问题。请问我做错了什么?

点赞
用户2425163
用户2425163

练习要求:

将标记文档中的所有常规文本转换为大写(无需触及 URL 或链接标题中的文本)。

可以按照上面所述的方式完成:

local text = require 'text'
function Str (s)
  s.text = text.upper(s.text)
  return s
end

这会保留 URL 和链接标题不变。


保留链接文本则有些困难。Pandoc Lua 过滤器以深度优先后序遍历文档树,因此只有在处理其内容之后才会处理 Link 节点。我们可以用简单的过滤器验证并可视化该过程,例如:

function Inline (i)
  print(i.tag, pandoc.utils.stringify(i))
end

对于输入 Hello, [Free Encyclopedia](https://en.wikipedia.org),运行上述代码会产生以下输出:

Str     Hello,
Space
Str     Free
Space
Str     Encyclopedia
Link    Free Encyclopedia

使用 Inlines 而不是 Inline 并没有区别:在我们知道它们属于哪个元素之前,嵌套的元素已经被处理完毕。这实际上意味着我们(很难)无法阻止转换影响特定子树。

这很不幸(同时,作为 Lua 过滤器系统的作者,这是我未来想要改变的东西)。但是,并不是所有东西都消失了。我们可以通过一个简单的技巧来解决这个问题:保存,然后还原原始链接内容:

local text = require 'text'
local links = pandoc.List()

function to_allcaps (s)
  s.text = text.upper(s.text)
  return s
end

function save_link (l)
  links:insert(l)
end

function restore_link (l)
  return links:remove(1)
end

return {
  {Link = save_link},
  {Str = to_allcaps},
  {Link = restore_link},
}

在这里,我们遍历文档三次,如返回的过滤器列表中的三个独立过滤器所示。首先,我们将所有链接收集到一个列表中;然后我们将所有文本转换为大写;最后,我们还原原始链接,从而撤消其链接标题中的所有大写修改。

简洁版:

local text = require 'text'
local links = pandoc.List{}
return {
  {Link = function (l) links:insert(l) end},
  {Str = function (s) return pandoc.Str(text.upper(s.text)) end},
  {Link = function (_) return links:remove(1) end},
}
2020-07-24 21:03:11