为什么这个 LaTeX 警告会导致无限循环?

为什么这个 LaTeX 警告会导致无限循环?

考虑以下 MWE,重现以下问题这个问题

\documentclass{article}

\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}

\usepackage{kotex}
\usepackage{amsmath}

\newtheorem{Cvičení}{Cvičení}

\begin{document}

\tracingmacros2\tracingcommands2

\begin{align*}
    a = b
\end{align*}

\end{document}

这里的问题是使用特殊字符作为环境名称\newtheorem。虽然很快就发现了这一点,但我发现出现的警告和错误有些模糊:

! Missing \endcsname inserted.
<to be read again> 
                   \protect 
l.16 \end{align*}

The control sequence marked <to be read again> should
not appear between \csname and \endcsname.

! TeX capacity exceeded, sorry [input stack size=5000].
\@spaces ->\space 
                  \space \space \space 
l.16 \end{align*}

If you really absolutely need more capacity,
you can ask a wizard to enlarge me.

即使没有使用\begin{Cvičení},这些错误也会出现,只有kotex加载 时才会出现。


我使用\tracing上面 MWE 中的命令进行了调查,发现日志文件的相关部分是第 5038 行及以下:

\@elt #1->\global \csname c@#1\endcsname \the \csname c@#1\endcsname 
#1<-CviÄení
{\csname}

Ä#1->\ifcsname u8:\string Ä\string #1\endcsname \csname u8:\string Ä\string #1\
expandafter \endcsname \else \expandafter \unihangul@two@octets \expandafter Ä\
expandafter #1\fi 
#1<-
{\ifcsname}
{\string}
{\string}
{true}
{\csname}
{\string}
{\string}
{\expandafter}
{\else}

\u8:Ä ->\IeC {\v c}

\IeC ->\ifx \protect \@typeset@protect \expandafter \@firstofone \else \noexpan
d \IeC \fi 
{\ifx}
{true}
{\expandafter}
{\else}

\@firstofone #1->#1
#1<-\v c

\v ->\T1-cmd \v \T1\v 

\T1-cmd #1->\ifx \protect \@typeset@protect \@inmathwarn #1\else \noexpand #1\e
xpandafter \@gobble \fi 
#1<-\v 
{\ifx}
{true}

\@inmathwarn #1->\ifmmode \@latex@warning {Command \protect #1 invalid in math 
mode}\fi 
#1<-\v 
{\ifmmode}
{true}

\@latex@warning #1->\GenericWarning {\space \space \space \@spaces \@spaces \@s
paces }{LaTeX Warning: #1}
#1<-Command \protect \v  invalid in math mode

\GenericWarning ->\csname m@gobble\iffirstchoice@ \else 4\fi \endcsname \protec
t \GenericWarning  
{\csname}
{\iftrue}
{true}
{\else}

\m@gobble ->

! Missing \endcsname inserted.
<to be read again> 
                   \protect 
l.16 \end{align*}

The control sequence marked <to be read again> should
not appear between \csname and \endcsname.


\GenericWarning  #1#2->\begingroup \def \MessageBreak {
#1}\set@display@protect \immediate \write \@unused {
#2\on@line .
}\endgroup 
#1<-\space \space \space \@spaces \@spaces \@spaces 
#2<-LaTeX Warning: Command \protect \v  invalid in math mode

\space -> 

\space -> 

\space -> 

\@spaces ->\space \space \space \space 

\space -> 

\space -> 

\space -> 

\space -> 

\@spaces ->\space \space \space \space 

\space -> 

\space -> 

\space -> 

\space -> 

\@spaces ->\space \space \space \space 

\space -> 

\space -> 

\space -> 

\space -> 

\set@display@protect ->\let \protect \string 
{\string}

\v ->\T1-cmd \v \T1\v 

我从中得到的信息如下:

  1. 环境align*导致许多计数器使用进行全局更新\@elt,包括(在日志文件中\c@Cvičení显示为)。\c@CviÄení­

  2. Ä是一个主动的角色(这似乎是负责的部分kotex)并最终导致被\v扩大。

  3. 由于\v不能在数学模式下使用,LaTeX 会尝试就此发出警告。

  4. 然后就出错了。\csname m@gobble\iffirstchoice@ \else 4\fi \endcsname \protect \GenericWarning导致\m@gobble扩展为无(尽管它的名字没有吞噬任何东西),但 TeX 似乎仍然想读取\csname并抱怨\protect(导致我们看到的警告)。

    • 尽管同一行代码在接下来的循环中被一次又一次地扩展,但这种情况只会发生一次。
  5. LaTeX 现在继续扩展\GenericWarning,并最终尝试将“命令 \v 在数学模式下无效”写入日志文件,本质上是说出来\let\protect\string然后写入Command \protect \v invalid in math mode日志文件。根据日志文件 ( {string}),这应该有效,但随后还是\v被扩展了。

  6. 这会导致无限循环,直到超出 TeX 的内存容量,导致最终的错误。


所以我的问题是

  • 为什么 4. 中的问题只出现一次,而不是在同一行代码的后续扩展中出现?

  • 为什么\let\protect\string \protect\v5.基本执行没有达到预期的效果呢?

答案1

第一个问题:为什么 4. 中的问题只出现一次,而不是在同一行代码的后续扩展中出现?

警告说“缺少插入的 \endcsname。... 标记的控制序列不应出现在 \csname 和 \endcsname 之间。”所以问题是 LaTeX 仍在等待\endcsname,因为第一个从未到达,但\endcsname(隐藏在这里的名称下)不允许出现在 csname 中。这不再是问题,因为就像消息所说的那样,TeX 实际上在显示错误之前插入了一个,所以在稍后我们不再处于 csname 中,所以(或)是有效的。\@elt\relax\protect\endcsname\relax\protect

第二个问题:为什么\let\protect\string \protect\v5.基本执行没有起到预期的效果?

为此,您必须进一步查看日志文件:在展开第一个 之前大约 10 行\@elt,有一行{\xdef}。因此,我们处于 的\xdef定义中\@gtempa。在 的扩展值中\xdef(或\edef),允许使用诸如 之类的不可展开命令\let,但保持原样,不进行评估。

因此,在这种情况下\let\protect\string\protect\v不会评估\let\protect,但会尝试扩展可扩展命令\string\v从而导致无限递归。

相关内容