为什么 pdfTeX 会挂在这个文件上?

为什么 pdfTeX 会挂在这个文件上?

梅威瑟:

\documentclass{minimal}
\usepackage{commath, tikz}
\begin{document}
\begin{tikzpicture}
\node {$:$};
\end{tikzpicture}
\end{document}

当我编译这个文件时,pdfTeX 进入无限循环。为什么?

答案1

commath问题是 TikZ 覆盖了(我不建议使用)给出的活动冒号的定义。

这应该可以:

\documentclass{minimal}
\usepackage{commath,tikz}
\makeatletter
\protected\def\tikz@nonactivecolon{\ifmmode\mathrel{\mathop\ordinarycolon}\else:\fi} 
\makeatother

\begin{document}
\begin{tikzpicture}
\node {$:$};
\end{tikzpicture}
\end{document}

mathtools请注意,如果加载了,也会发生同样的情况,并且

\mathtoolsset{centercolon}

被使用。但同样的解决方法也有效。


这是一个独立于已加载的包运行的版本,当然假设已经tikz加载了该包。

\documentclass{article}

\usepackage[french]{babel}
\usepackage{mathtools}\mathtoolsset{centercolon}
%\usepackage{commath}
\usepackage{tikz}


\usepackage{etoolbox}

\makeatletter
\AtEndPreamble{
  \ifnum\mathcode`\:=\string"8000
    \begingroup\lccode`\~=`\:
    \lowercase{\endgroup\let\math@colon@meaning~}
  \else
    \expandafter\let\expandafter\math@colon@meaning\string:
  \fi
}
\AtBeginDocument{
  \ifnum\catcode`\:=\active
    \letcs\text@colon@meaning{active@char\string:}
  \else
    \expandafter\let\expandafter\text@colon@meaning\string:
  \fi
\protected\def\tikz@nonactivecolon{%
  \ifmmode
    \expandafter\math@colon@meaning
   \else
    \expandafter\text@colon@meaning
   \fi} 
\begingroup\lccode`\~=`\:
\lowercase{\endgroup\let~\tikz@nonactivecolon}
}
\makeatother

\begin{document}

French: colon

\begin{tikzpicture}
\node {$:=$};
\end{tikzpicture}
\end{document}

答案2

到目前为止,我对最初的答案做了三处修改:

  1. 我添加了一些代码来解决 Babel 引发的问题,当它使一个字符(这里是冒号)活跃时,其他一些包也使它在数学上活跃;我想确保\label并且\ref可以在所有情况下使用(包括语言切换),这很困难。

  2. egreg在第一次编辑中,我基本上也从补丁复制到了\tikz@nonactivecolon。在第二次编辑中,我更详细地阐述了这个 TikZ 补丁,因为我也希望\label\ref可以在 tikz 图片中使用,并且具有潜在的活动 + 数学上的活动冒号。

  3. 我回来时有了简化 Babel 补丁的想法,但后来我意识到它缺少了一个内容\fi;然后它变得非常混乱,因为TeX当我添加它时出现了错误,我意识到它没有正确地\else与相应的\if......[更新:事实上,我犯了一个编码错误,这个错误源于我一直对TeX扫描条件语句时到底做了什么的误解。我吸取了教训]...我把所有的东西都移到了一个专用宏中,这个宏是首先定义的...[这确实是对这个实现问题的正确修复]。

  4. 好的,我确实有一个较短的版本,\dotheactivecatcodesubroutine我将其附加在本答案的最底部。我没有尝试解释,因为它让我头疼。它似乎工作得很好。我保留了原来的。要研究它们中的任何一个的效果,可以向文档正文中添加一些内容,例如

    {\tt家庭

    \expandafter\meaning\csname 用户@active\string:\endcsname

    \expandafter\meaning\csname normal@char\string:\endcsname}

    根据是否mathtools加载来查看补丁对 Babel 做了什么。

  5. 我在这个答案的末尾添加了TikZ补丁的一个轻微变体,用于 (Babel) catcode 激活和 mathcode 激活冒号的情况。这使用 。这似乎也有效,并且具有优点或缺点,即冒号现在充当图片内部(和数学模式外部)的\bbl@deactivate法语(或其他语言使其激活)冒号。nodetikz


commath当问题出现时,说它写得不好有点苛刻,因为tikz没有检查冒号的数学激活!

Babel 也有同样的问题。试试

\documentclass{article}
\usepackage[french]{babel}
\mathcode`:="8000
{\catcode`:\active \gdef:{\ifmmode A\else B\fi}}
\begin{document}

a:b

\end{document}

到目前为止一切顺利。在文档开头,冒号通过frenchb.sty的服务变为活动状态babel。您可以看到冒号前面的额外空格。

然后尝试将$a:b$代码添加到:它陷入了无限循环。为什么?因为babel+frenchb定义活动冒号以扩展到后面跟着的某个空间\string:,即带有类型的冒号other(类别代码 12),这应该是安全的。

not在数学模式下它一定是安全的!因为我们可以让字符被声明为数学活动,这意味着虽然不一定是类别代码 13,但它仍然(大部分)表现得像它一样。所以\string:看起来很安全的其实不是!试试这个

\documentclass{article}

\mathcode`:="8000

\catcode`:\active \gdef:{\ifmmode MATH\else TEXT\fi}

\begin{document}

: % active colon gives TEXT

$:$ % active colon gives MATH

\string: % category 12 colon gives ... colon

$\string:$ % prints MATH! this shows that it was treated as being active!

\end{document}

现在commath可以对其数学活动冒号进行所有您想要的定义,但它们都无法解决问题! 所做的定义commath与无限循环无关! 纯粹是因为冒号在数学上是活动的,导致了无限循环,因为人们认为(由tikz或 由frenchb+babel)第 12 类字符是安全的。

确实,补丁egreg应用于的宏,而tikz不是的宏commath


我一直在努力为 Babel+math 活动字符和 TikZ(这要容易得多)提供补丁。我发布了它,尽管一些问题很可能仍然存在(我现在很累,工作速度越来越慢,直到深夜......)。我应该说我把所有的精力都花在了 Babel 上,而关于 TikZ,我只是看得很快。所以我可能忽略了很多东西(尤其是因为我从未使用过 TikZ。)Babel 有很多困难的事情,但最棘手的是当一个字符(这里是冒号)在数学上是活动的,并且是 catcode 活动的,但随后用户切换语言并且该字符在新语言中是正常的(但它仍然是 catcode 活动的)。解决方案必须与在数学模式下使用\label和兼容\ref。Babel 在处理活动字符时非常精致和小心,但根本不关注数学模式。(除了插入符号或素数之类的东西)。(我说已经晚了,所以我最后一句话可能太快和肤浅了)

我不使用etoolbox

\documentclass[fleqn]{article}

\usepackage[english,french]{babel} % can be commented out
\usepackage{mathtools}\mathtoolsset{centercolon} % can be commented out
\usepackage{tikz}

% this goes  BEFORE \begin{document} and AFTER all packages
% such as mathtools which may let : be mathematically active
% (hopefully they do it already in the preamble).

\makeatletter
  \ifnum\mathcode`\:=\string"8000   
    \begingroup\lccode`\~=`\:  % and the tilde ~ must be active here.
    \lowercase{\endgroup
               \let\mathactive@colon@meaning~}
  \fi
\makeatother

\makeatletter

   % The next macro will be used at begin document (so after Babel
   % has done its activation of characters, depending on the language)

   % I had to move it here because of absolutely uncomprehensible
   % problems with the parsing of the conditionals 
   %    (adding braces changed nothing, I used `tracingcommands2` to 
   % confirm that TeX was taking the wrong `\else` but so far I have 
   % not understood why.)

   % If Babel is not loaded what is done here does no harm.
   % But it does no good either. 
   %
   % Note: recent versions of frenchb.ldf do not use active characters 
   % under XeTeX  but \XeTeXinterchartoks, which are no-op in math
   % mode
   %
   % The characters activated by Babel do not check for math mode (except
   % possibly the caret and the prime and maybe a few others, I should 
   % check in babel.pdf), it is up to the language definition
   % file to do so. For example the
   % extra spacing in front of !?;: is put by `frenchb`
   % only in horizontal mode, not
   % in math mode. In math mode the colon is just a standard category 12
   % colon.
   %
   % But this creates an infinite loop is the colon is also 
   % mathematically active!
   %
   % The following cures this. It will let the
   % colon act either as the standard category 12 colon or, if
   % mathtools or another package has made the colon mathematically
   % active (in the preamble) it will then use that definition.
   %
   % Let's store the Babel active and non-active versions,
   % [nota bene: the ``normal char''  is the meaning to which the
   % active character expands when the user switches languages to one
   % where the character is not activated. The character remains
   % active in the sense of catcode throughout the document except if
   % the user uses \shorthandoff which really sets the catcode to 12.
   % The following should be compatible with language changes. ]
   %
   % Babel uses \active@char: to let \user@active: become
   % \normal@char: when it is used inside \ref, \newlabel, \bibcite...
   % so to benefit from this we store the current
   % meaning of \user@active:, and we do not modify \active@char:.
   %
   % The Babel definition of the active character
   % puts a prefix in front of \active@char: (or \normal@char:) which
   % results in first expansion of the active character : 
   % to be itself preceded
   % by a \protect or \noexpand, or sometimes \active@char:. (so\label{eq:1}
   % works ).
   %
   % On the other hand $\ref{eq:1}$ works for another reason:
   % the \active@char: then expands to be \normal@char: hence
   % to a category code 12 :. The mathcode is not a problem because this
   % is inside \csname...\endcsname. 
   %
   % If the user has switched to a language where the colon is
   % `normal', the character remains active but when not
   % protected expands to \normal@char: hence a category 12 :. And
   % we are then doomed if the mathcode is 32768. Infinite loop.
   %
   % As \ref{eq:1} will make use of \normal@char: which then must expand
   % to a category 12 colon, our possibilities to modify \normal@char:
   % are greatly limited. As Babel has patched \ref and similar macros
   % to set the \if@safe@actives flag to be true, we can test for it.
   % We thus leave \normal@char: untouched except if \if@safe@actives if
   % false (this takes care of \ref)  and if \protect is
   % \@typeset@protect: and only then we let \normal@char: use
   % \mathactive@colon@meaning.
   % 
\def\dotheactivecatcodesubroutine{%
      \expandafter\let\expandafter\original@user@active@colon
          \csname user@active\string:\endcsname
      \expandafter\let\expandafter\original@normal@char@colon
          \csname normal@char\string:\endcsname 
          % should just be : with catcode 12
    \ifnum\mathcode`\:=\string"8000
        \expandafter\def\csname user@active\string:\endcsname{%
          \ifmmode
             \expandafter\mathactive@colon@meaning
          \else
             \expandafter\original@user@active@colon
          \fi} 
        \def\hip@hop##1##2##3##4##5{##2##3##4##1}
        \expandafter\def\csname normal@char\string:\endcsname{%
           \ifmmode
            \if@safe@actives\else
             \ifx\protect\@typeset@protect % <-- this is probably superfluous
              \hip@hop\mathactive@colon@meaning
             \fi
            \fi
           \fi
           \original@normal@char@colon}
    \else 
         % here we have no problem of mathcode, but we would also like to
         % annihilate (in a reasonable manner) the effects in math
         % mode of the Babel activation of the character (here the colon).
        \expandafter\def\csname user@active\string:\endcsname{%
          \ifmmode
             \expandafter\original@normal@char@colon
           \else
             \expandafter\original@user@active@colon
          \fi}
    \fi}

\AtBeginDocument{
   \ifnum\catcode`\:=\active  % We assume it is Babel+frenchb which is
                              % responsible for this
     \dotheactivecatcodesubroutine
   \fi
     %
     % if the character is not active, I will assume it will not be
     % activated later by Babel (I will have to check the
     % documentation). But we still have some problems
     % with TikZ.... Indeed it seems that tikz
     % `deactivates' the colon by re-defining its active version to
     % expand to the category 12 colon:
     %     \def:{\tikz@nonactivecolon}% see tikz.code.tex 1521--1525&1439
     % In the process the mathtools definition is thus overwritten
     % but the mathcode is left to 32768... so the colon in math
     % expands exactly as if it was active hence to
     % \tikz@nonactivecolon which expands to the category 12 colon
     % but don't forget the mathcode.... INFINITE LOOP.
   \ifnum\mathcode`:=\string"8000
      % if the colon is not mathematically active we don't have
      % to do anything additional for TikZ now.
     \@ifpackageloaded{tikz}
     {%
      % We have to distinguish the case of a catcode active colon.
      % We are in that case in deep trouble
      % Because TikZ does not try to modify the catcode (perhaps
      % impossible due to argument parsing, I don't know) but
      % redefines the active colon, we have to make sure this definition
      % is safe with the math active colon. For this we simulate a
      % switch to a language where the colon is `normal' in the sense
      % of Babel, because we already solved the problem there. So we
      % import that solution (of course this is assuming that the
      % colon is active because of Babel in the first place).
      % 
      % The following was done to make sure \label and \ref were usable
      % inside tikz pictures, but perhaps this is not licit practice?
      % (I don't know TikZ)
      \ifnum\catcode`:=\active
         \begingroup\lccode`~=`:
         \lowercase{\endgroup
              \let\original@babelactivecolon~
              \g@addto@macro\tikz@deactivatthings{%
                   \expandafter\expandafter\expandafter\let
                   \expandafter\expandafter
                       \csname active@char\string:\endcsname
                       \csname normal@char\string:\endcsname
                   \def~{\original@babelactivecolon}}}
      \else % colon is not catcode active 
      % in this branch the active colon will only be invoked in math mode
      % because of the mathcode
      % as the mathcode is not looked at in an \edef, \write, or a
      % \csname.. \endcsname, I think I don't need the e-TeX \protected
        \let\original@tikz@nonactivecolon\tikz@nonactivecolon
        \def\tikz@nonactivecolon{%
          \ifmmode
             \expandafter\mathactive@colon@meaning
          \else
             \expandafter\original@tikz@nonactivecolon
          \fi}
      \fi}{}
    \fi % end of TikZ patching for colon with mathcode 32768
} % end of AtBeginDocument

\makeatother

\begin{document}
\newcounter{testcounter}

\makeatletter
\@ifundefined{shorthandon}{}{French}%
\makeatother
: colon (:)

Math $:= (:)$ colon 

Tikzpicture

\begin{tikzpicture}
\node {\refstepcounter{testcounter} a:b $ \label{pic:1} a:b $};
\end{tikzpicture}

\begin{tikzpicture}
\node {\refstepcounter{testcounter} c:d $ \label{pic:2} a:b \ref{pic:1}$};
\end{tikzpicture}

\begin{equation}
  \label{eq:1}
  a := b
\end{equation}

\begin{equation}
  \label{eq:2}
  b := a\quad (\mbox{see equation}\ \ref{eq:1})
\end{equation}

At the end of this manuscript we will have established equation \ref{eq:2}
starting from equation \ref{eq:1}.

\makeatletter
\@ifundefined{shorthandon}{}{\shorthandoff{:}not French anymore}%
\makeatother
: colon (:)

Math $:= (:)$ colon 

Tikzpicture

\begin{tikzpicture}
\node {\refstepcounter{testcounter} d:e $ \label{pic:3} a:b \ref{pic:2}$};
\end{tikzpicture}

\begin{tikzpicture}
\node {\refstepcounter{testcounter} f:g $ \label{pic:4} a:b \ref{pic:3}$};
\end{tikzpicture}

\begin{equation}
  \label{eq:3}
  c := a\quad (\mbox{to be deduced from}\ \ref{eq:2})
\end{equation}


\makeatletter

\@ifundefined{shorthandon}{}{\shorthandon{:}}

\@ifundefined{shorthandon}{}{\selectlanguage{english}English}%
\makeatother
: colon (:)


Math $:= (:)$ colon 

Tikzpicture

\begin{tikzpicture}
\node {\refstepcounter{testcounter} k:l $ \label{pic:5} a:b \ref{pic:4}$};
\end{tikzpicture}

\begin{tikzpicture}
\node {\refstepcounter{testcounter} m:n $ a:b \ref{pic:5}$};
\end{tikzpicture}


\begin{equation}
  \label{eq:4}
  c := b\quad (\mbox{to be deduced from}\ \ref{eq:3})
\end{equation}


\end{document}

替代版本\dotheactivecatcodesubroutine

\def\dotheactivecatcodesubroutine{%
      \expandafter\let\expandafter\original@user@active@colon
          \csname user@active\string:\endcsname
      \expandafter\let\expandafter\original@normal@char@colon
          \csname normal@char\string:\endcsname 
          % should just be : with catcode 12
      \expandafter\expandafter\expandafter
         \def\expandafter\expandafter
             \csname user@active\string:\endcsname  
               \expandafter{\expandafter
                 \ifmmode
                    \expandafter\expandafter
                    \csname normal@char\string:\endcsname
                 \else
                    \expandafter\original@user@active@colon
                 \fi} 
        \def\hip@hop##1##2##3##4##5{##2##3##4##1}
        \ifnum\mathcode`\:=\string"8000
        \expandafter\def\csname normal@char\string:\endcsname{%
           \ifmmode
            \if@safe@actives\else
             \ifx\protect\@typeset@protect % <-- this is probably superfluous
              \hip@hop\mathactive@colon@meaning
             \fi
            \fi
           \fi
           \original@normal@char@colon}
        \fi
}

该版本的TikZ补丁更简单。它的效果似乎是它像另一个补丁一样避免了错误,但这次冒号在 中充当了法式冒号node。我没有深入研究,tikz.code.tex但似乎确实如此\tikz@installcommands,然后\tikz@uninstallcommands重新建立了活动冒号的初始定义。避免了无限循环。

 \@ifpackageloaded{tikz}
 {%
  \ifnum\catcode`:=\active
     \begingroup\lccode`~=`:
     \lowercase{\endgroup
          \g@addto@macro\tikz@deactivatthings{\bbl@deactivate~}}%
  \else
    \let\original@tikz@nonactivecolon\tikz@nonactivecolon
    \def\tikz@nonactivecolon{%
      \ifmmode
         \expandafter\mathactive@colon@meaning
      \else
         \expandafter\original@tikz@nonactivecolon
      \fi}%
  \fi}{}

答案3

我花了几个小时调试这个。正如@egreg 已经指出的那样在他的评论中commath是一个写得不好的包。它试图通过以下方式修复 := 的对齐:

\mathchardef\ordinarycolon\mathcode`\:
\mathcode`\:=\string"8000
\begingroup \catcode`\:=\active
   \gdef:{\mathrel{\mathop\ordinarycolon}}
\endgroup

不幸的是,这个 hack 并不完美,会导致 TikZ 节点无限循环。我不知道错误的确切解释,但你最好将命令定义从 commath.sty 复制到你的序言中,或者创建 .sty 文件的本地副本并删除这些行。

是否有人知道类似的 hack 来修复 := 并且它也可以在 TikZ 节点中使用?

相关内容