niszetの日記

アナログCMOS系雑用エンジニアが頑張る備忘録系日記

PandocのLuaフィルターを書いてみる

Luaの文法がまだ覚えきれていないのでまずはそっちを固めるべき

ひとまず、公式のマニュアルを読んだ。

pandoc.org

例はココにあるらしいが、まだ読んでいない。

github.com

また、通常のフィルターも少しだけ目を通したが、フィルタの実行環境を持っていない人にどうやって環境を渡すのかを考えるのが面倒なので、Pandoc2.0から本体に組み込まれ、そして他の言語よりも動作が速いと書かれていたLuaのフィルターを使うこととした。

pandoc.org

最近Luaの勉強をしていたのはそのためである ^[もうひとつ、FlashAirのためでもあるのだが]

マニュアルに書かれていないことが多くないですかね…。

とりあえずマニュアル読んだだけだと非常に簡単なものしか作れない。ちょっと習作ということで、強調を斜体に変更し、間にあるスペースを削除して詰めるフィルタを書いた。結果としてそうなったというだけで、はじめからそうしたかったわけではないのだが。

function Strong(elem)
  t = ""
  for k, v in pairs(elem.content) do
    if v.tag == "Str" then
        t = t .. v.text
    end 
  end
  return {pandoc.Emph(t)}
end

これで、**HU HU HU HU**と書いたものが*HUHUHUHU*と等価な出力となった。

この変換前の一文は

"C:/Program Files/RStudio/bin/pandoc/pandoc" -f markdown hoge.md -t native

のようにnative形式で出力すると

Para [Strong [Str "HU",Space,Str "HU",Space,Str "HU",Space,Str "HU"]]

に対応する。ここで、elem.contentはこの[Str "HU",Space,Str "HU",Space,Str "HU",Space,Str "HU"]を受け取ることになる。

さて、コードについて。function Strong(elem)でStrongが見つかったらこの関数をcallするような挙動 ^[実装がそうなっているのかは確認していない。]となる。ここでelemを引数に入れないとどうなるんだろ?あとでやってみる。

for文で各要素を見ていく。ここでkは要素のindex、vが実体になるようだ。この場合、ipairsの方が良いのかもしれない。あとでやってみる。

Spaceはtextを要素に持っていないのでnilが返る。nilと文字列は結合できないので先にvのtagがStrであることを確認している。もっと良い実装があるだろうけど。

  t = ""
  for k, v in pairs(elem.content) do
    if v.tag == "Str" then
        t = t .. v.text
    end 
  end

で、テキストを結合している。最初に空の文字列を与えないとこれまたnilとの結合になるので注意。Luaはundefinedは全部nilだ。

最後はtableにして返さないとダメっぽいが、ここは理由がちょっとよくわからない。他の例では大丈夫だったはずなのに。

  return {pandoc.Emph(t)}

ということで、はじめてのLuaフィルタの完成なのでした。

Word出力にする際にちょっとあれこれいじりたいところがあるので、Luaのfilterを使って何とかしたかったのですが、これならすぐできるかもしれないね。

Enjoy!!