ltjsarticleで\endcsnameが無いと言われる

ltjsarticleで\endcsnameが無いと言われる

- Nakata Masato の投稿
返信数: 14
複数の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の使い方が違うのでしょうか?
Nakata Masato への返信

Re: ltjsarticleで\endcsnameが無いと言われる

- 前田 一貴 の投稿
> ltjsclassesでは\@forの使い方が違うのでしょうか?
そんなことはないはずです.

提示されたコードを試してみましたが,私の環境では特にエラーは出ませんでした.
ただし,TeX Live 2018 pretest で試しています.

他の読み込んでいるパッケージや sample.tex の中身が影響しているかも?
Nakata Masato への返信

Re: ltjsarticleで\endcsnameが無いと言われる

- 若 雲 の投稿
ただの再現の報告ですが、Windows 10、TeXLive 2017で同様のエラーが出ます。
添付 includeall_error.png
若 雲 への返信

Re: ltjsarticleで\endcsnameが無いと言われる

- 前田 一貴 の投稿
別の PC で,同じ TeX Live 2018 pretest ですが試したところ,件のエラーが出ました.
少し調べてみます.

あと,sample.tex の中身が書かれていたのを見落としていて,頓珍漢な返答をしていました.失礼いたしました.
sample.tex 内の \LaTeX 命令が原因でエラーが出ているみたいです.
前田 一貴 への返信

Re: ltjsarticleで\endcsnameが無いと言われる

- 前田 一貴 の投稿
さっぱりわかりませんが,\include に渡す \next を展開しておくと通るみたいです.

\documentclass{ltjsarticle}
\makeatletter
\newcommand{\includeall}[1]{\@for\next:=#1\do{\expandafter\include\expandafter{\next}}}
\makeatother
\begin{document}
\includeall{sample}
\end{document}
Nakata Masato への返信

Re: ltjsarticleで\endcsnameが無いと言われる

- Z. R. の投稿

\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@}}

などの処置が必要になります。

Z. R. への返信

Re: ltjsarticleで\endcsnameが無いと言われる

- 前田 一貴 の投稿
・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
                                         
?
前田 一貴 への返信

Re: ltjsarticleで\endcsnameが無いと言われる

- Z. R. の投稿
LuaTeX-ja の \lower 上書きのせいですね.

そういえば、そんなことをやってましたね……。

普通のTeX原語プログラミングの感覚だと、「プリミティブを実行して指定外の制御綴の内容が変わる」のは想定外になりそうです。プリミティブの再定義をする場合は、非ユニークな制御綴は避けた方がよさそうですね。

Z. R. への返信

Re: ltjsarticleで\endcsnameが無いと言われる

- 北見 けん の投稿
なるほど。
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 の再定義はうっかりミスかなと思いました。
おそらく修正されるのでしょうね。
開発に携わっている方々には、感謝するばかりです。
北見 けん への返信

Re: ltjsarticleで\endcsnameが無いと言われる

- 前田 一貴 の投稿
解説ありがとうございます.

\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 などに全て変えると,
件のソースでもエラーが出なくなりました.
北見 けん への返信

Re: ltjsarticleで\endcsnameが無いと言われる

- aminophen の投稿
今回の件とは外れますが,
・plext.sty の \bou
・jsclasses の \@footnotetext
にも内部で \next を使っています。
衝突が怖いとすると,これも unique な名前に変えた方が
いいでしょうか。
(プリミティブではないですが。)
aminophen への返信

Re: ltjsarticleで\endcsnameが無いと言われる

- Z. R. の投稿

あくまで一つの考えとしてですが。

「プリミティブが非ユニークな制御綴を上書きすべきでない」の一般化として「既存のマクロが持っている良い性質は、再定義によって壊すべきではない」という考えがあります。

これに基づくと、

jsclasses の \@footnotetext

については、「元の(LaTeXカーネルの)\@footnotetext\next を上書きしない」ため、その性質に依拠するコードが(原理的には)存在しえます。それを考えるとここでは \next は避けた方がいいかもしれません。

一方で、\bou は何かの再定義ではないので、この制約には当たらないことになります。

※もちろん、\next だけ特別なのではなくて、\x\@tempa も「\@tempcnta の値」も全て同様です。

※私自身は、“安全側に倒す”プログラミングをするので、原則的に、“外側の”マクロを実行したときは非ユニークな値は保持されないことを仮定します。それでも流石に、「プリミティブが非ユニークな値を破壊する」のは想定してません。

Z. R. への返信

Re: ltjsarticleで\endcsnameが無いと言われる

- 北見 けん の投稿
上のほうのコメントに、こういう注意事項はまあ浸透していると書きましたが、
やはりみなさん、他のマクロとの干渉が起こらないように気をつけていますよね。

手法としては、私も、下のコメントで本田さんが書かれているように、
ユニークな名前のものをグローバルに使うよりは
グルーピングで痕跡を消す方をよく使います。
(ユニークな名前といっても完全に100%保障されることはないというのと、
 同じマクロを入れ子にして使った場合などに困りそうだというのが理由です)

また、内部で自作以外のマクロ(LaTeX標準も含む)を使う場合はかなり警戒しますが、
プリミティブについては警戒しない というのは、Z. R. さんと同様ですね。

## 変数のアクセス制御に関しては、あると便利で安全ですから
## 仕事で使う場合は良いですよね。
## 私はもっぱらただの趣味なので、不便なほど楽しみが増します。

みなさんのご意見がうかがえてよかったです。
Nakata Masato への返信

Re: ltjsarticleで\endcsnameが無いと言われる

- Nakata Masato の投稿
皆様ありがとうございます.

エラーの原因はltjsclassesが\nextを意図せず使っていたことだったのですか.
これはレアケースとはいえ,今後,一時変数の名前はすべてuniquenessを遵守したい思います.
Nakata Masato への返信

Re: ltjsarticleで\endcsnameが無いと言われる

- 本田 知亮 の投稿
やっぱり名前空間とかエクスポートとか
アクセス制御的なものがほしいという気も
しないでもないですが。。。

一時的なものを作る手としては

・\@ifundefinedの類をこまめに使う
・\begingroup~\endgroupの類でグループを作って
その内部で局所的に好き勝手する.
その中の結果を外に持ち出したいときは
\aftergroupとか「\expandafter\egroup\x」のトリックを使う(\globalは極力使わない)
・eclarithのように独自の「文字」を使って
特殊なコントロールシーケンスを使う
(LaTeXが@を使うのと同じようにeclarithは!を使ってる)

というようなものを私は使いますが,
一番重宝してるのは二つ目の方法です.

あとはそこまで,支給されたデータに
あとから上書きするようなときは
\typeout{\meaning\hogehohe}で
\hogehogeがそこで何か怪しげなものになってないか
チェックすることもたまにします.