マクロツイーター

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

zxjatype でアレな環境を使うとアレな件(1)

アレのことだけど。

zxjatype じゃなくて xeCJK を指定して同様の文書を作った場合にも同じエラーが発生するので、これは xeCJK の問題といえるだろう。で、きちんと調べたわけではないが、何となく原因と考えられるものを述べておく。これは XeTeX 特有の機能である「文字間トークン自動挿入」に関連する。

同様のエラーの再現例

次の(ToL の)コードは、「文字間トークン自動挿入」を利用して、文字〈3〉が自動的にアホなフォントになる仕掛けを作っている。(〈3〉の出力の前後で自動的に「\begingroup\ahofont」および「\endgroup」のトークン列が挿入される。)また、環境 hoge は単に中身のフォントをサンセリフにするものであるが、ここでは \newenvironment 命令を利用せずに、“原始的”に \hoge\endhoge のマクロを定義している。

% XeLaTeX 文書; 文字コードは UTF-8
\documentclass[a4paper]{article}
%% 環境 hoge を"原始的に"定義する
\def\hoge{\sffamily}
\let\endhoge\relax
%% 文字〈3〉をアホなフォントにする
\font\ahofont=cmfi10 at 15pt
\XeTeXcharclass`3=9
\XeTeXinterchartoks 0 9 {\begingroup\ahofont}
\XeTeXinterchartoks 255 9 {\begingroup\ahofont}
\XeTeXinterchartoks 9 0 {\endgroup}
\XeTeXinterchartoks 9 255 {\endgroup}
\XeTeXinterchartokenstate=1
\begin{document}
12345 \begin{hoge}123\end{hoge}45
\end{document}

この文書のコンパイルは成功し、機体通りの結果が得られる。ところが、ここで上掲のソース中で、\endhoge の定義文をコメントアウトして、\endhoge を未定義の状態にしてみる。

%%\let\endhoge\relax % \endhoge は未定義

LaTeX の内部仕様としては、これでも環境の定義としては正当である。実際、例の TeX Forum の記事では、「宣言型命令は環境としても使用可能である」という LaTeX の規則に従って、文字サイズ命令 \Huge を環境として使っているのであるが、このような“環境”に関しては、もちろん \endHuge というマクロは定義されていない。

さて、この修正を加えた後で再び文書をコンパイルしてみると、今度は、\endhoge が未定義だというエラーが出てしまう。

! Undefined control sequence.
<recently read> \endhoge

l.15 12345 \begin{hoge}123\end{hoge}
                                    45
?

何故だろうか? エラーが出る直前の“3\end{hoge}”の箇所の処理を追いかけてみる。

  1. まず、(展開不能な)文字トークン〈3〉が実行対象になる。文字〈2〉を出力した直後なので、〈2〉‐〈3〉の組での文字間トークン列挿入が起こる。クラスの組は 0‐9 なので、「\begingroup\ahofont」が挿入される。
  2. \begingroup\ahofont」が実行される。(ここでグループに入ることに注意。)
  3. 文字〈3〉の出力を行う。(フォントは cmfi10 になっている。)
  4. 次のトーク\end は展開可能(マクロ)なので、展開される。
    \end{hoge}
    → \csname endhoge\endcsname \@checkend {hoge}…(略)…
    
  5. 展開後の先頭トーク\csname は展開可能プリミティブなので、展開される。
    \csname endhoge\endcsname
    → \endhoge
    
    ここで、TeX の仕様として、\csname で制御綴を生成した場合に、それが未定義であった場合は暗黙的に \relax に等置されるので、ここで \endhoge\relax になる。((ちなみに、この「\relax 化」は TeX において「展開によって代入動作が引き起こされる」唯一の事例である。))
  6. 展開後の先頭トーク\endhoge は展開不能\relax だから)なので、ここで文字間トークン列挿入が起こる。クラスの組は 9‐255 なので*1、「\endgroup」が挿入される。
  7. \endgroup が実行される。2 で開始したグループを脱出する。
  8. 次のトークンは \endhoge である。さて、これの現在の意味は何だろうか? 5 において、\endhoge\relax に等置されたが、これは 2 で開始したグループの中においてであり、そのグループはさっき抜けたので、\endhoge未定義に戻っている
  9. 結果として、「\endhoge が未定義である」というエラーが発生する。

要約すると、\endhoge という制御綴の生成とその実行の間でトークン列が割り込んできてそれでグループレベルが変わってしまうのが原因、ということになる。LaTeX の実装コードが XeTeX 特有の機能との絡みで問題が出るのは仕方がないことであろう。

確証は得られていないが、TeX Forum で報告されている xeCJK の事例でも同様の動作が起こっているものと思われる。\end の直前に \relax\relax の処理の時となって、\endhoge の時ではなくなるからである。

*1:文字以外のトークンはクラス 255 と扱われる。