解包开始时的奇怪 catcode 设置

解包开始时的奇怪 catcode 设置

unravel在包源代码中我们可以发现一些神秘的 catcode 设置:

\begingroup\let\c\catcode\fam32\c\fam10\advance\fam5\c\fam14\c45 12 %
\c54 12\c55 12\c56 12\c57 12\c58 11\c95 11\c104 11\c106 11\c107 11 %
\c113 11\c115 11\c119 11\c120 11\c121 11\c122 11\endlinechar-1 %
\expandafter\ifx\csname unravel\endcsname\relax
\else\endinput\expandafter\endgroup\fi
\c84 11\c88 11\c35 6\c123 1\c125 2\c62 12\c61 12\c43 12 %

软件包文档解释了这里发生的情况:

Catcode 设置。在组中,设置\c为 的同义词\catcode,将 space 的 catcode 设置为 10(使用\fam以避免需要空格或等号来分隔 的两个整数参数\catcode),将 的catcode%设置为 14(再次使用以避免需要数字 7 来使 catcode 成为 other:无论如何,我们都需要分两步\fam获得数字)。然后将、、、、设置为other(我们必须假设 到已经是 other),并将、、、、、 、、、 、 、、、字母设置为字母(其他小写字母在其余代码中已经需要为字母)。确保没有。我们终于可以安全地测试包是否已经加载,并在已经加载的情况下退出。5-678905:_hjkqswxyz\endlinechar

我仍然不清楚这样做的确切目的。该包似乎这样做是为了检查该包是否已经加载。因此有些其他还有一些字符以一种巧妙的方式重置为它们的正常 catcode。据我所知,它仍然必须依赖于许多其他字符具有它们的正常 catcode。

也许我在这里漏掉了什么,但如果是这样的话,那是不是意味着整个 catcode 的更改几乎毫无用处?一半的数字和字母仍然具有正确的 catcode,而另一半需要重置的可能性在我看来非常低。有人能解释一下为什么这是必要的或要做的吗?

答案1

\fam是内部整数寄存器,因此\fam32将其设置为 32;然后\advance\fam5将其设置为 37。在哪里\fam使用?在数学模式下,它被初始化为,但可以将其设置为-1数学系列之一(),以便更改类型 7 的符号字母表(通常为字母)。015

如果我们解开代码,我们得到

\catcode 32=10 % ensure space has catcode 10
\catcode 37=14 % ensure % is comment (but without using the digit 7
% the digits from 0 to 5 have been used, assume they have catcode 12
% set other catcodes to their standard value
\catcode 45=12 % hyphen, necessary for -1
\catcode 54=12 % 6
\catcode 55=12 % 7
\catcode 56=12 % 8
\catcode 57=12 % 9
\catcode 58=11 % colon 
\catcode 95=11 % underscore
\catcode 104=11 % h
\catcode 106=11 % j
\catcode 107=11 % k
\catcode 113=11 % q
\catcode 115=11 % s
\catcode 119=11 % w
\catcode 120=11 % x
\catcode 121=11 % y
\catcode 122=11 % z
\endlinechar-1 %
\expandafter\ifx\csname unravel\endcsname\relax
  % \unravel is not defined
\else
  % assume unravel.sty has already been loaded, bail out
  \endinput\expandafter\endgroup\fi % \fi needs to be on the same line as \endinput
\catcode 84=11 % T
\catcode 88=11 % X
\catcode 35=6 % #
\catcode 123=1 % {
\catcode 125=2 % }
\catcode 62=12 % >, necessary for \ifnum
\catcode 61=12 % <, necessary for \ifnum
\catcode 43=12 % +

还有其他代码如下:

\expandafter\ifx\csname numexpr\endcsname\relax
\errmessage{unravel requires \numexpr from eTeX}
\endinput\expandafter\endgroup\fi
\expandafter\ifx\csname protected\endcsname\relax
\errmessage{unravel requires \protected from eTeX}
\endinput\expandafter\endgroup\fi
\expandafter\ifx\csname currentgrouplevel\endcsname\relax\else
\ifnum\currentgrouplevel>1 \errmessage{unravel loaded in a group}
\endinput\expandafter\expandafter\expandafter\endgroup\fi\fi

第一个\ifx用于在 e-TeX 不可用时退出。请注意,此部分代码中使用了已设置为标准 catcode 的字符。接下来检查组级别是否为 1,否则unravel将加载到组内,无法工作。

据我所知,该包对类别代码做出了最小可能的假设。它需要\类别代码为 0,abcdefgilmnoprtuv类别代码为 11(对于\begingroup\let\c\catcode和),数字类别代码为 12。如果不是,那就没什么可做的了。使用而不是 不会节省任何东西;此外\fam,是\advance012345\bgroup\begingroup\bgroup定义,而\begingroup是原始的。

当然,如果某个早于加载的包unravel重新定义了\begingroup,,\let否则我们\catcode\fam 完了。

其他编程语言拒绝重新定义重要的关键字或函数名称,但 TeX 却不会,并且很乐意服从那些想要搬起石头砸自己脚的程序员。

可以想象,如果 LaTeX 内核具有某些设置类别代码的功能(比如宏),则需要做出更少的假设,\aaaaaaaaaaaaaaaaaaaaaaaa\需要类别代码 0 和a类别代码 11。但是,也可以想象一些狡猾的用户在代码中看到该宏并重新定义它。

相关内容