複数のtexファイルをまとめてincludeするために次のようなコマンドを定義して使っています:
\newcommand{\includeall}[1]{\@for\next:=#1\do{\include{\next}}}
uplatex + jsarticleやlualatex + articleだとうまくいくのですが,lualatex + ltjsarticleだとエラーになります.
% root.tex
\documentclass{ltjsarticle}
\makeatletter
\newcommand{\includeall}[1]{\@for\next:=#1\do{\include{\next}}}
\makeatother
\begin{document}
\includeall{sample}
\end{document}
% sample.tex
Hello, \LaTeX!
$ lualatex root.tex
This is LuaTeX, Version 1.0.4 (TeX Live 2017)
restricted system commands enabled.
(./root.tex
LaTeX2e <2017-04-15>
(using write cache: /Users/username/Library/texlive/2017/texmf-var/luatex-cache/
generic)(using read cache: /usr/local/texlive/2017/texmf-var/luatex-cache/gener
ic /Users/username/Library/texlive/2017/texmf-var/luatex-cache/generic)
luaotfload | main : initialization completed in 0.218 seconds
Babel <3.18> and hyphenation patterns for 1 language(s) loaded.
(/usr/local/texlive/2017/texmf-dist/tex/luatex/luatexja/ltjsarticle.cls
Document Class: ltjsarticle 2018/01/14 ltjsclasses
(/usr/local/texlive/2017/texmf-dist/tex/luatex/luatexja/luatexja.sty
(/usr/local/texlive/2017/texmf-dist/tex/luatex/luatexja/luatexja-core.sty
(/usr/local/texlive/2017/texmf-dist/tex/luatex/luatexbase/luatexbase.sty
(/usr/local/texlive/2017/texmf-dist/tex/luatex/ctablestack/ctablestack.sty))
(/usr/local/texlive/2017/texmf-dist/tex/generic/oberdiek/ltxcmds.sty)
(/usr/local/texlive/2017/texmf-dist/tex/generic/oberdiek/pdftexcmds.sty
(/usr/local/texlive/2017/texmf-dist/tex/generic/oberdiek/infwarerr.sty)
(/usr/local/texlive/2017/texmf-dist/tex/generic/oberdiek/ifluatex.sty)
(/usr/local/texlive/2017/texmf-dist/tex/generic/oberdiek/ifpdf.sty))
(/usr/local/texlive/2017/texmf-dist/tex/latex/xkeyval/xkeyval.sty
(/usr/local/texlive/2017/texmf-dist/tex/generic/xkeyval/xkeyval.tex
(/usr/local/texlive/2017/texmf-dist/tex/generic/xkeyval/xkvutils.tex
(/usr/local/texlive/2017/texmf-dist/tex/generic/xkeyval/keyval.tex))))
(/usr/local/texlive/2017/texmf-dist/tex/generic/oberdiek/atbegshi.sty)
(/usr/local/texlive/2017/texmf-dist/tex/latex/etoolbox/etoolbox.sty)
(/usr/local/texlive/2017/texmf-dist/tex/latex/everyhook/everyhook.sty
(/usr/local/texlive/2017/texmf-dist/tex/latex/svn-prov/svn-prov.sty))(load cach
e: /Users/username/Library/texlive/2017/texmf-var/luatexja/ltj-cid-auto-adobe-ja
pan1.luc) (/usr/local/texlive/2017/texmf-dist/tex/luatex/luatexja/ltj-base.sty)
(/usr/local/texlive/2017/texmf-dist/tex/luatex/luatexja/ltj-latex.sty
(/usr/local/texlive/2017/texmf-dist/tex/luatex/luatexja/patches/lltjfont.sty
(/usr/local/texlive/2017/texmf-dist/tex/latex/base/tuenc.def)
(/usr/local/texlive/2017/texmf-dist/tex/latex/ms/everysel.sty)
ABD: EverySelectfont initializing macros)
(/usr/local/texlive/2017/texmf-dist/tex/luatex/luatexja/patches/lltjdefs.sty(lo
ad luc: /Users/username/Library/texlive/2017/texmf-var/luatex-cache/generic/font
s/otl/lmroman10-regular.luc)(load luc: /Users/username/Library/texlive/2017/texm
f-var/luatex-cache/generic/fonts/otl/ipaexm.luc)(load cache: /Users/username/Lib
rary/texlive/2017/texmf-var/luatexja/extra_ipaexmincho.luc)
(/usr/local/texlive/2017/texmf-dist/tex/luatex/luatexja/jfm-ujisv.lua)
(/usr/local/texlive/2017/texmf-dist/tex/luatex/luatexja/jfm-ujis.lua))
(/usr/local/texlive/2017/texmf-dist/tex/luatex/luatexja/patches/lltjcore.sty)
(/usr/local/texlive/2017/texmf-dist/tex/luatex/luatexja/patches/lltjp-geometry.
sty (/usr/local/texlive/2017/texmf-dist/tex/latex/filehook/filehook.sty))))
(/usr/local/texlive/2017/texmf-dist/tex/luatex/luatexja/luatexja-compat.sty(loa
d cache: /Users/username/Library/texlive/2017/texmf-var/luatexja/ltj-jisx0208.lu
c)))(load luc: /Users/username/Library/texlive/2017/texmf-var/luatex-cache/gener
ic/fonts/otl/lmroman8-regular.luc)
(/usr/local/texlive/2017/texmf-dist/tex/platex/jsclasses/jslogo.sty)
(/usr/local/texlive/2017/texmf-dist/tex/latex/sttools/stfloats.sty))
(./root.aux (./sample.aux))
(/usr/local/texlive/2017/texmf-dist/tex/luatex/luatexja/patches/lltjp-stfloats.
sty) ABD: EverySelectfont initializing macros (./sample.tex(load luc: /Users/na
katam/Library/texlive/2017/texmf-var/luatex-cache/generic/fonts/otl/lmroman7-re
gular.luc))
! Missing \endcsname inserted.
<to be read again>
\next
l.6 \includeall{sample}
? ^D
! Emergency stop.
<to be read again>
\next
l.6 \includeall{sample}
4414 words of node memory still in use:
8 hlist, 1 vlist, 4 rule, 1 local_par, 1 dir, 2 math, 4 glue, 54 kern, 12 gl
yph, 1678 attribute, 48 glue_spec, 92 attribute_list, 1 temp, 5 if_stack, 2 wri
te, 11 user_defined nodes
avail lists: 1:4,2:111,3:1,4:4,5:4,7:2,8:4,9:2
! ==> Fatal error occurred, no output PDF file produced!
Transcript written on root.log.
ltjsarticleの他にltjsbookやltjsreport,ltjarticleでも同じエラーを確認しました.
ltjsclassesでは\@forの使い方が違うのでしょうか?
\include{\next}
とすると、「処理対象のファイル名は \next.tex
である」と扱われるようです。そして、「処理対象のファイル名に依存する処理が残存している段階で \next
の値が書き換わってしまう」と、その処理は正常に実行できなくなります。(\next
は非ユニークであるため上書きされる可能性があります。)これがエラーの原因のようです。
従って、「上書きされないようにユニークな制御綴を使う」
\newcommand{\includeall}[1]{% \@for\my@next:=#1\do{\include{\my@next}}}
か、または「あらかじめ引数を完全展開する」
\newcommand{\includeall}[1]{% \@for\my@next:=#1\do{% \edef\my@next@{\noexpand\include{\my@next}}\my@next@}}
などの処置が必要になります。
・LuaTeX-ja が \next を内部で使っている
・このせいで \LaTeX を出力しようとすると \next が書き換わってしまっておかしくなる
ということでしょうか.ありがとうございます.
ちなみに,\LaTeX が引き金になっているのですが,もう少し試してみると
\lower.5ex\hbox{}
だけで引き起こせるみたいです.LuaTeX-ja の \lower 上書きのせいですね.
sample.tex を
Hello \show\next\lower.5ex\hbox{}\show\next
にしてみるとよくわかりますね.
> \next=macro:
->sample.
l.1 Hello \show\next
\lower.5ex\hbox{}\show\next
?
> \next=\hbox.
l.1 Hello \show\next\lower.5ex\hbox{}\show\next
?
・このせいで \LaTeX を出力しようとすると \next が書き換わってしまっておかしくなる
ということでしょうか.ありがとうございます.
ちなみに,\LaTeX が引き金になっているのですが,もう少し試してみると
\lower.5ex\hbox{}
だけで引き起こせるみたいです.LuaTeX-ja の \lower 上書きのせいですね.
sample.tex を
Hello \show\next\lower.5ex\hbox{}\show\next
にしてみるとよくわかりますね.
> \next=macro:
->sample.
l.1 Hello \show\next
\lower.5ex\hbox{}\show\next
?
> \next=\hbox.
l.1 Hello \show\next\lower.5ex\hbox{}\show\next
?
なるほど。
LuaTeX は未導入なのでROMしていました。←死語かな?ROM
(「TeXを使ってみよう」で試せればうれしいですが)
今回のエラーのプロセスをまとめると、こんな感じでしょうか。
1. \include は、概ね
\def\include#1{....\input{#1.tex}....\input{#1.aux}...}
のような感じでファイル名を複数個所にコピーする。
2. 今回は \@for\next:=... によって一旦ファイル名を \def\next{sample}としてから
\include\next としたために ...\input{\next.tex}...\input{\next.aux}... となる。
3. \input{\next.tex}で 読み込まれた sample.tex の中にたまたま \LaTeX があった。
\LaTeX マクロが展開されるなかで \lower が用いられている。
4. luatexja-ja.pdf の「6.4 プリミティブの再定義」にあるように、
LuaTeX-ja では \lower などが再定義されていて、
その内部で \next を一時マクロとして書き換えてしまう。
5. sample.tex の読み込みが終了し、次に \input{\next.aux} のところでは、
すでに \next の意味が変わってしまっていて、エラーとなる。
教訓としては、
(1) 上記の 2.のところで \include のような
自作以外のマクロと組み合わせて作業させるときは、
一時マクロにもユニークな名称を使う。
(2) 上記 4. のプリミティブの再定義のように影響が大きいものでは
内部の一時マクロはユニークな名称のものを使う。
の二点ですね。どちらもまあ浸透している注意事項なので、
この \lower の再定義はうっかりミスかなと思いました。
おそらく修正されるのでしょうね。
開発に携わっている方々には、感謝するばかりです。
LuaTeX は未導入なのでROMしていました。←死語かな?ROM
(「TeXを使ってみよう」で試せればうれしいですが)
今回のエラーのプロセスをまとめると、こんな感じでしょうか。
1. \include は、概ね
\def\include#1{....\input{#1.tex}....\input{#1.aux}...}
のような感じでファイル名を複数個所にコピーする。
2. 今回は \@for\next:=... によって一旦ファイル名を \def\next{sample}としてから
\include\next としたために ...\input{\next.tex}...\input{\next.aux}... となる。
3. \input{\next.tex}で 読み込まれた sample.tex の中にたまたま \LaTeX があった。
\LaTeX マクロが展開されるなかで \lower が用いられている。
4. luatexja-ja.pdf の「6.4 プリミティブの再定義」にあるように、
LuaTeX-ja では \lower などが再定義されていて、
その内部で \next を一時マクロとして書き換えてしまう。
5. sample.tex の読み込みが終了し、次に \input{\next.aux} のところでは、
すでに \next の意味が変わってしまっていて、エラーとなる。
教訓としては、
(1) 上記の 2.のところで \include のような
自作以外のマクロと組み合わせて作業させるときは、
一時マクロにもユニークな名称を使う。
(2) 上記 4. のプリミティブの再定義のように影響が大きいものでは
内部の一時マクロはユニークな名称のものを使う。
の二点ですね。どちらもまあ浸透している注意事項なので、
この \lower の再定義はうっかりミスかなと思いました。
おそらく修正されるのでしょうね。
開発に携わっている方々には、感謝するばかりです。
解説ありがとうございます.
\documentclass{article}
\usepackage{filehook}
\begin{document}
\input{\hbox}
\end{document}
で ! Missing \endcsname inserted. が出ることを確認しました.
LuaTeX-ja 内での問題の \next については,マクロを
\lower -> \ltj@@raise -> \ltj@afterbox
とたどっていくと,ltj-base.sty に
%% \ltj@afterbox <token><box>
%% -> \setbox\ltj@afbox<box><token>
%% from Sonja Maus, ``Looking Ahead for a <box>'',
%% TUGBoat, 11, No. 4, 1990.
\newbox\ltj@afbox
\protected\def\ltj@afterbox#1{%
\def\ltj@afb@xarg{#1}%
\afterassignment\ltj@afb@x
\chardef\next`.}
\def\ltj@afb@x{\futurelet\next\ltj@afb@xtest}
\def\ltj@afb@xtest{%
\ifcase\ifx\next\hbox\tw@\fi
\ifx\next\vbox\tw@\fi
\ifx\next\vtop\tw@\fi
\ifx\next\box\@ne\fi
\ifx\next\copy\@ne\fi
\ifx\next\vsplit\@ne\fi
\ifx\next\lastbox\@ne\fi
0% ``A <box> was ...'' error will be causes by \setbox later anyway.
\or\afterassignment\ltj@afb@xarg
\or\afterassignment\ltj@afb@xagarg
\fi
\setbox\ltj@afbox
}
\def\ltj@afb@xagarg{\aftergroup\ltj@afb@xarg}
があるのが見つかり,これらが原因のようです.\next を \ltj@next などに全て変えると,
件のソースでもエラーが出なくなりました.
\documentclass{article}
\usepackage{filehook}
\begin{document}
\input{\hbox}
\end{document}
で ! Missing \endcsname inserted. が出ることを確認しました.
LuaTeX-ja 内での問題の \next については,マクロを
\lower -> \ltj@@raise -> \ltj@afterbox
とたどっていくと,ltj-base.sty に
%% \ltj@afterbox <token><box>
%% -> \setbox\ltj@afbox<box><token>
%% from Sonja Maus, ``Looking Ahead for a <box>'',
%% TUGBoat, 11, No. 4, 1990.
\newbox\ltj@afbox
\protected\def\ltj@afterbox#1{%
\def\ltj@afb@xarg{#1}%
\afterassignment\ltj@afb@x
\chardef\next`.}
\def\ltj@afb@x{\futurelet\next\ltj@afb@xtest}
\def\ltj@afb@xtest{%
\ifcase\ifx\next\hbox\tw@\fi
\ifx\next\vbox\tw@\fi
\ifx\next\vtop\tw@\fi
\ifx\next\box\@ne\fi
\ifx\next\copy\@ne\fi
\ifx\next\vsplit\@ne\fi
\ifx\next\lastbox\@ne\fi
0% ``A <box> was ...'' error will be causes by \setbox later anyway.
\or\afterassignment\ltj@afb@xarg
\or\afterassignment\ltj@afb@xagarg
\fi
\setbox\ltj@afbox
}
\def\ltj@afb@xagarg{\aftergroup\ltj@afb@xarg}
があるのが見つかり,これらが原因のようです.\next を \ltj@next などに全て変えると,
件のソースでもエラーが出なくなりました.
あくまで一つの考えとしてですが。
「プリミティブが非ユニークな制御綴を上書きすべきでない」の一般化として「既存のマクロが持っている良い性質は、再定義によって壊すべきではない」という考えがあります。
これに基づくと、
jsclasses の \@footnotetext
については、「元の(LaTeXカーネルの)\@footnotetext
は \next
を上書きしない」ため、その性質に依拠するコードが(原理的には)存在しえます。それを考えるとここでは \next
は避けた方がいいかもしれません。
一方で、\bou
は何かの再定義ではないので、この制約には当たらないことになります。
※もちろん、\next
だけ特別なのではなくて、\x
も \@tempa
も「\@tempcnta
の値」も全て同様です。
※私自身は、“安全側に倒す”プログラミングをするので、原則的に、“外側の”マクロを実行したときは非ユニークな値は保持されないことを仮定します。それでも流石に、「プリミティブが非ユニークな値を破壊する」のは想定してません。
上のほうのコメントに、こういう注意事項はまあ浸透していると書きましたが、
やはりみなさん、他のマクロとの干渉が起こらないように気をつけていますよね。
手法としては、私も、下のコメントで本田さんが書かれているように、
ユニークな名前のものをグローバルに使うよりは
グルーピングで痕跡を消す方をよく使います。
(ユニークな名前といっても完全に100%保障されることはないというのと、
同じマクロを入れ子にして使った場合などに困りそうだというのが理由です)
また、内部で自作以外のマクロ(LaTeX標準も含む)を使う場合はかなり警戒しますが、
プリミティブについては警戒しない というのは、Z. R. さんと同様ですね。
## 変数のアクセス制御に関しては、あると便利で安全ですから
## 仕事で使う場合は良いですよね。
## 私はもっぱらただの趣味なので、不便なほど楽しみが増します。
みなさんのご意見がうかがえてよかったです。
やはりみなさん、他のマクロとの干渉が起こらないように気をつけていますよね。
手法としては、私も、下のコメントで本田さんが書かれているように、
ユニークな名前のものをグローバルに使うよりは
グルーピングで痕跡を消す方をよく使います。
(ユニークな名前といっても完全に100%保障されることはないというのと、
同じマクロを入れ子にして使った場合などに困りそうだというのが理由です)
また、内部で自作以外のマクロ(LaTeX標準も含む)を使う場合はかなり警戒しますが、
プリミティブについては警戒しない というのは、Z. R. さんと同様ですね。
## 変数のアクセス制御に関しては、あると便利で安全ですから
## 仕事で使う場合は良いですよね。
## 私はもっぱらただの趣味なので、不便なほど楽しみが増します。
みなさんのご意見がうかがえてよかったです。
やっぱり名前空間とかエクスポートとか
アクセス制御的なものがほしいという気も
しないでもないですが。。。
一時的なものを作る手としては
・\@ifundefinedの類をこまめに使う
・\begingroup~\endgroupの類でグループを作って
その内部で局所的に好き勝手する.
その中の結果を外に持ち出したいときは
\aftergroupとか「\expandafter\egroup\x」のトリックを使う(\globalは極力使わない)
・eclarithのように独自の「文字」を使って
特殊なコントロールシーケンスを使う
(LaTeXが@を使うのと同じようにeclarithは!を使ってる)
というようなものを私は使いますが,
一番重宝してるのは二つ目の方法です.
あとはそこまで,支給されたデータに
あとから上書きするようなときは
\typeout{\meaning\hogehohe}で
\hogehogeがそこで何か怪しげなものになってないか
チェックすることもたまにします.
アクセス制御的なものがほしいという気も
しないでもないですが。。。
一時的なものを作る手としては
・\@ifundefinedの類をこまめに使う
・\begingroup~\endgroupの類でグループを作って
その内部で局所的に好き勝手する.
その中の結果を外に持ち出したいときは
\aftergroupとか「\expandafter\egroup\x」のトリックを使う(\globalは極力使わない)
・eclarithのように独自の「文字」を使って
特殊なコントロールシーケンスを使う
(LaTeXが@を使うのと同じようにeclarithは!を使ってる)
というようなものを私は使いますが,
一番重宝してるのは二つ目の方法です.
あとはそこまで,支給されたデータに
あとから上書きするようなときは
\typeout{\meaning\hogehohe}で
\hogehogeがそこで何か怪しげなものになってないか
チェックすることもたまにします.