マクロツイーター

はてダから移行した記事の表示が崩れてますが、そのうちに直せればいいのに(えっ)

verbatim な入力を Lua 関数で簡単に扱う件について (1)

[2012-06-08編集]IXbase バンドルを BXluatool バンドルとして再編した。それに合わせて記事の内容も書き直した。

かなり昔に「思わず Lua で LaTeX してみた」で紹介したように、LuaLaTeX においては、「Lua コードの呼出」を利用することで、LaTeX のマクロ(ユーザ定義命令)の知識だけでかなり複雑な処理ができる。*1LaTeX のマクロで実装すべきなのは、「LaTeX の命令に渡された引数を適切に Lua の関数に渡す」ということだけである。しかし、ここで「LaTeX のマクロ(\newcommand 等)」では解決できないタイプの構造がある。それが verbatim な入力である。そこで、LuaLaTeX において verbatim 入力の扱いを可能にするパッケージを作ってみた。

注意 「思わず Lua で…」の解説では、(La)TeX特殊文字を含む Lua コードを記述するのに、ixbase0 という独自のパッケージを用意してそこで提供される execluacodeblock 環境を用いていた。今では、同じ機能を luacode* 環境として提供する luacode パッケージが CTAN にある(W32TeX にも TeX Live にも収録済)ので、それを用いるのがよいであろう。本記事でも、ixbase0 でなく、luacode を用いることにする。なお、ixbase0 について、luacode と重複する機能を整理したのが同じバンドルに含まれる bxluafacade と bxluatangle である。

このパッケージは次の命令を提供する。

  • \DeclareVerbInputEnvironment{<名前>}[<整数n>]{<本体>}: n−1 個の(非 verbatim な)引数を伴う、中身を verbatim に扱う環境(「verbatim入力環境」と呼ぶ)を指定の名前で定義する。環境の処理内容は本体部で指定され、ここは n 個の引数をもつ命令の定義(\newcommand)と同様の方式で記述する。一番最後の引数(#n)は環境の中身の文字列((カテゴリコードは(空白文字も含めて)全て 12 であり、改行は CR(^^M)として表される。))を表し、それ以外の(n−1 個の)引数は環境に渡された実際の引数を現す。n の既定値は 1 (つまり引数のない環境に対応する)である。

最も基本的なパターンが、引数のない環境 hoge があって、その中身の文字列を(verbatim に)Lua の関数 foo に渡せばよいという場合である。これは次のようにして実現できる。

\DeclareVerbInputEnvironment{foo}{%
  % #1 に環境の中身の文字列が入っている
  \directlua{ foo(\luastringN{#1}) }%
}

\luastringN は文字列を Lua の文字列リテラルの形に変換するための luacode の命令である。((つまり、「\luastringN{\textbf{A}}」は「"\\textbf {A}"」に展開される。ixbase0 の \ixstring 命令と同じ。なお、この記事を書いた当初は \luastring としていたが、これは引数を完全展開してから文字列リテラルに変換するので、\ixstring とは異なる。無論、verbatim 入力については展開しても何も変わらない。))環境の中身 #1Lua 関数に渡せたので、後は好きなように Lua コードでそれを処理することができる。多くの場合、tex.sprint()((tex.sprint() は引数の文字列の各々を「単純に」順に TeX 入力として書き出す。「思わず…」で紹介した tex.print() と異なり「行として扱う」処理を行わない。通常は sprint の方が使い易いであろう。)) で LaTeX 側に何か「結果」を返すことになるだろう。

より完全な例を挙げておく。以下の例では、makeshorthands という環境を定義している。これは複数の「略記用」の命令を一度に定義するためのもので、

% 引数は命令名の接頭辞
\begin{makeshorthands}{zzz}
aaa=bbbb
ccc=dddd  # コメント
…(同様の形式の行が続く)
\end{makeshorthands}

のように用いると、

\newcommand*{\zzzaaa}{bbbb}
\newcommand*{\zzzccc}{dddd}
……

という LaTeX 命令が実行され複数の命令が定義される。

\documentclass{article}
\usepackage{bxluavienv}
\usepackage{luacode}
\DeclareVerbInputEnvironment{makeshorthands}[2]{%
  \directlua{ % Lua 関数に引数を丸投げするだけ
    make_shorthands(\luastring{#1}, \luastring{#2})
  }
}
\begin{luacode*}
-- Lua 関数が実際の仕事をしている
shorthands = {}
function make_shorthands (prefix, datatable)
  for _, row in ipairs(datatable:explode("\r+")) do
    local key, val = row:gsub("%s.*#.*", ""):match("^%s*(%a+)=(.*)$")
    if not key then error("bad line: "..row) end
    local csname = "\\"..prefix..key
    tex.sprint("\\newcommand*{", csname, "}{", val, "}")
  end
end
\end{luacode*}
\begin{document}
% makeshorthands の用例
\begin{makeshorthands}{logo}
luatex=Lua\TeX
lualatex=Lua\LaTeX
context=Con{\TeX}t   # not have 'Lua' in name
texinfo=Texinfo      # yeah! ^^;
\end{makeshorthands}
This is \logolualatex!
\end{document}

make_shorthands() 関数の引数 datatable に渡されるのは、# 等の「特殊文字」もそのままに含まれる文字列である。((なお、改行は CR になるので、string.explode() の引数が "\n+" でなく "\r+" であることに注意。さらに、「中身そのまま」ということは、\begin のある行の末尾の改行も文字列に含まれることにも注意。(explode"\r+" 指定だとこれは問題にならない。)))ところで、出力においては、\LaTeX はちゃんと制御綴として認識されている(verbatim でない)が、これは、Lua から TeX に結果を書き出した時に(通常のカテゴリコードで)トークン化が行われるからである。

*1:一つ注意しておく。LaTeX において、TeX 言語の知識が必要になる主要なもう一つの場面として「文書のレイアウト設定」があるが、こちらは LuaTeX においても「TeX 言語の代わりに Lua で済ませる」ということがあまり期待できない。なぜなら、(現状の LaTeX2e の実装では)文書レイアウトの設定には、LaTeXカーネルの実装が深く絡んでいて、そして当然ながらこれは全て TeX 言語で実装されているからである。