マクロツイーター

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

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

[2012-06-05編集]記事の内容を書き直した。(参照

前回の続き)

verbatim な入力を LaTeX 上で気軽に扱えると、色々なことに応用できる。

verbatim な出力

例えば、verbatim な入力ができると、verbatim な出力ができる。

\documentclass{article}
\usepackage{fontspec} % Unicode フォントに切替
\usepackage{bxluavienv,luacode}
\begin{luacode*}
function verb_output(str)
  local lines = str:explode("\n") -- 「\r+」「\n+」でなく「\n」
  -- 入力文字列を行ごとに分割した上で
  -- 各行を「\mbox{}\\」区切りで出力する
  for k = 1, #lines do
    tex.sprint(-2, lines[k]) -- 第 1 引数に「-2」
    if k < #lines then tex.sprint("\\mbox{}\\\\") end
  end
end
\end{luacode*}
\DeclareVerbInputEnvironment{simpleverbatim}{%
  \par\noindent
  \directlua{verb_output(\VIEString{#1})}\par % \VIEString に注意
}
\begin{document}
\begin{simpleverbatim}
#1 discount & service
$5.99 {79% off!}
\end{simpleverbatim}
\end{document}

ここでのポイントは、tex.sprint() の第 1 引数に「−2」を渡していること。前回、tex.sprint() で書き出す時に「普通に」(= 現在のカテゴリコードで)トークンが行われると述べたが、「−2」を指定すると、書き出す時も verbatim なままになる。*1

また、Lua 関数に入力文字列を渡す時に、(luacode の)\luastring(N) でなく \SPEString を使っている。これは、「先頭が CR(^^M)だったら除去する」「それ以外の CR を LF(^^J)に置換する」という変換を行った文字列*2を表す。これを使うと、受け取る Lua 関数ではそれを「普通の複数行テキスト」のように取り扱えて便利である。

※注意: この simpleverbatim 環境は確かに「書いたままの Unicode 文字列」を現在のフォントで出力するのであるが、それが正しく出力されるかは、フォントのエンコーディングに依存する。当然 Unicode フォントならば大丈夫であり、また ASCII 文字に限れば T1 エンコーディングでもよいしまた(等幅 OT1 の)cmtt フォントでもよい。しかし OT1 の cmr フォントでは一部の文字が化ける。この文書では fontspec を読み込んでいるので Unicode フォントが使われている。*3

luacode 環境を自分で作る

中身の文字列が得られているので、それを \directlua に放り込むだけである。

\documentclass{article}
\usepackage{bxluavienv} % luacode 要らないよ
% luacode* 環境を自前で作る
\DeclareVerbInputEnvironment{myluacode}{%
  \directlua{#1}}% これでおしまい!
% それを使ってみる
\begin{myluacode}
function do_format(f, v) -- lua-style comment
  tex.sprint(string.format("%"..f, tonumber(v)))
end
\end{myluacode}
% おっと、\luastringN も必要でした...
\newcommand*{\luastringN}[1]{"\luatexluaescapestring{\detokenize{#1}}"}
%%<*> \format{XXX}{N} : printf 書式 %XXX で数値 N を出力
\newcommand*{\format}[2]{\directlua{do_format(\luastringN{#1},\luastringN{#2})}}
\begin{document}
The answer is \format{04X}{66}.
\end{document}
普通の LaTeX 環境を普通の命令のように定義する

bxluavienv パッケージには \ParseAsLaTeX という命令が用意されていて、verbatim に取得した文字列をこの命令に渡すことでもう一度トークン化して(普通の LaTeX のソースとして)実行することができる。これを利用すると、環境をあたかも命令であるかのように定義できて、しかも定義された環境は当然 \verb を含めるという利点が得られる。

\documentclass{article}
% 色を定義する
\usepackage{color}
\definecolor{mred}{rgb}{0.85,0,0}
\definecolor{lpink}{rgb}{1,0.85,0.85}
\usepackage{bxluavienv}
%%<*> specialbox 環境: 中身を色つきの箱で囲って出力する
% \ParseAsLaTeX{#1} は要するに環境の中身を普通に解釈して得られるトークン列。
\DeclareVerbInputEnvironment{specialbox}{%
  \fcolorbox{mred}{lpink}{\Large\textcolor{mred}{\ParseAsLaTeX{#1}}}}
\begin{document}
% 用例。意地悪く \verb 入り。
\begin{specialbox}
I $\heartsuit$ Lua\TeX! \verb|(^_^;|
\end{specialbox}
\end{document}

ちなみに、この機能を提供する既存のパッケージとして、environ パッケージ(Will Robertson 氏作製)がある。

*1:第 1 引数が 0 以上ならばカテゴリコードテーブルレジスタの番号を表す。−1、および省略時(第 1 引数が数値以外)は現在のカテゴリコードテーブルを用いる。

*2:実際には「そういう文字列を表す Lua の式」。

*3:fontspec 読込前の既定は OT1 の Computer Modern フォント。読込後の既定は Unicode の Latin Modern フォント。