解决 "Pandoc Filters" 页上的第一个练习

Solving the first exercise on "Pandoc Filters" page

第一个问题https://pandoc.org/filters.html#exercises asks to convert all text to uppercase except if it is part of a URL or a link title. So, I read the discussion about "Execution Order" in lua filters at https://pandoc.org/lua-filters.html#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] .. '====>' -- just so that I can see it in the document.
      -- elems[i].content = pandoc.Str 'hello'
    end
  end
  return elems
end

--[[ -- Explicitly force order of filters -- from "Execution Order" list...
return {
  { Link = Link,
    Str = Str,
    Inlines = Inlines
  }
}
]]

认为这会解决我的问题。但不知何故我无法让它发挥作用。我还尝试通过强制调用过滤器的顺序(在脚本末尾...注释)来明确安排 table,但它似乎不起作用。我做错了什么?

练习题:

Put all the regular text in a markdown document in ALL CAPS (without touching text in URLs or link titles).

这可以按照您上面描述的方式完成:

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

这样就只剩下 URL 和 link 标题了。


单独留下 link 文本有点困难。 Pandoc Lua 过滤器以 depth-first 后序遍历文档树,因此 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 过滤系统的作者,我想在未来改变这一点)。然而,并非所有都丢失了。我们可以通过一个简单的技巧解决这个问题:保存,然后恢复原始 link 内容:

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},
}

在这里,我们遍历文档三次,如返回的过滤器列表中的三个单独的过滤器所示。首先,我们将所有 link 收集到一个列表中;然后我们将所有内容全部大写;最后,我们恢复了原始的 links,从而撤消了其 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},
}