我仍在尝试了解 tex 究竟是如何解析文档的。这一次,我试图了解\newcommand
和 朋友中的所有参数标记处理,这对我来说就像黑魔法一样 - 这里的关键似乎发生在\@yargdef
和 中\@yargd@f
,分别可以在第 868 行和第 877 行中找到latex.ltx
。目的显然是创建最终将跟在 后面的参数字符串\def\foo
,但我不明白这是如何工作的。
为了解释我认为我的问题出在哪里,我不得不稍微解释一下我的想法,不幸的是。所以:如果我这样做\newcommand\foo[2]{\bar}
,那么据我所知,这最终(在多次检查是否\foo
已经定义并检查可选参数等之后)扩展为\@argdef\foo[2]{\bar}
。
我将逐步讲解如何解读这些定义:
\@argdef
定义如下:
所以我们最终\long\def\@argdef#1[#2]#3{% \@ifdefinable #1{\@yargdef#1\@ne{#2}{#3}}}
\@yargdef\foo\@ne{2}
(离开{\bar}
)。\@yargdef
定义如下:
它定义\long \def \@yargdef #1#2#3{% \ifx#2\tw@ \def\reserved@b##11{[####1]}% \else \let\reserved@b\@gobble \fi \expandafter \@yargd@f \expandafter{\number #3}#1% }
\reserved@b
为\@gobble
,最终扩展为\@yargd@f{2}\foo
(离开{\bar}
)。- 现在
\@yargd@f
是最奇怪的部分。它的定义如下:
因此,就我的情况而言,扩展应该是(如果我没记错的话?):\long \def \@yargd@f#1#2{% \def \reserved@a ##1#1##2##{% \expandafter\def\expandafter#2\reserved@b ##1#1% }% \l@ngrel@x \reserved@a 0##1##2##3##4##5##6##7##8##9###1% }
因此在应用中\def\reserved@a #12#2#{% \expandafter\def\expandafter\foo\reserved@b #12% }% \l@ngrel@x \reserved@a 0#1#2#3#4#5#6#7#8#9#2
\reserved@a
,第一个参数应该是0#1#
,第二个参数应该是......空的,我猜,因为紧随其后的标记已经是#
......?3#4#5#6#7#8#9#2{\bar}
作为下一个标记保留?
这肯定是我的想法出了错,因为如果我假装之前的字符串{\bar}
不是在那里,剩下的就说得通了:
\reserved@a
然后扩展为
\expandafter\def\expandafter\foo\reserved@b 0#1#2
-\reserved@b
被定义为\@gobble
只吃掉它找到的第一个标记,所以它的扩展应该是\def\foo#1#2
- 接下来是{\bar}
。
所以显然我对参数字符串的解释是\reserved@a
错误的?如果不是空的,那么它的应用中的第二个参数到底是什么\reserved@a
?我能想到的唯一可能的其他解释是,它吞噬了下一个 #
-token,但即便如此,它也只会吃掉部分#3
并留下一整串参数标记……?
我是否扩大了定义0##1##2##3##4##5##6##7##8##9###1
或者##1#1##2##
定义了\reserved@a
错误?
答案1
除了#{
参数的作用之外,您的推理是正确的。
当 TeX在宏的#
中看到 a 时(如),后面的标记可以是 1-9 范围内的数字,也可以是。当参数是“可怕的怪异参数”时,TeX 的行为就像前一个参数的分隔符是 a 一样(因为你不能在 中使用 a )。<parameter text>
\def\macro<parameter text>{<replacement text>}
{
#{
{
{
<parameter text>
在这个定义中(记住{\bar}
仍然在输入流中):
\def\reserved@a #12#2#{%
\expandafter\def\expandafter\foo\reserved@b #12%
}%
\l@ngrel@x \reserved@a 0#1#2#3#4#5#6#7#8#9#2{\bar }
当\reserved@a
扩展时,#1
将会是,正如你所说,,0#1#
并且#2
将是直到下一个的一切{
,并且不是为空,所以,由中#3#4#5#6#7#8#9#2
的 分隔。 的这个定义实际上抛弃了剩余的参数标记。因此,你“假装”在 之前的字符串{
{\bar }
\reserved@a
{\bar}
不是那里”实际上是正确的。其余代码按您的假设执行。
引自TeXbook,关于#{
参数:
允许对这些规则进行特殊扩展:如果 的最后一个字符
<parameter text>
是#
,因此#
紧接着{
,TeX 将表现得好像{
已插入参数文本和替换文本的右端。例如,如果您说 '\def\a#1#{\hbox to #1}
',则后续文本 '\a3pt{x}
' 将扩展为 '\hbox to 3pt{x}
',因为 的参数\a
由左括号分隔。
答案2
我已经在我的 LaTeX 编程书中处理过这个问题了。我的例子略有不同,但更容易复制。;-) 假设我们有
\newcommand{\xyz}[2]{ab#1cd#2ef}
这变成
\@star@or@long\new@command[2]{ab#1cd#2ef}
由于没有*
after \new@command
,因此 this 确实存在\let\l@ngrel@x=\long
并\@star@or@long
消失了。现在\new@command
被扩展,产生
\@testopt{\@newcommand\xyz}0[2]{ab#1cd#2ef}
这变成
\kernel@ifnextchar[{\@newcommand\xyz}{\@newcommand\xyz[{0}]}[2]{ab#1cd#2ef}
目的是[0]
在未给出参数数量时添加。由于有[
,我们得到
\@newcommand\xyz[2]{ab#1cd#2ef}
现在开始查找另一个可选参数,我跳过了它,我们得到
\@argdef\xyz[2]{ab#1cd#2ef}
检查token 的\xyz
可定义性;如果不可定义,我们会收到错误消息,否则
\@yargdef\xyz\@ne{2}{ab#1cd#2ef}
真正的乐趣就从这里开始:
\ifx\@ne\tw@
\def\reserved@b#11{[##1]}
\else
\let\reserved@b\@gobble
\fi
\expandafter\@yargd@f\expandafter{\number2}\xyz{ab#1cd#2ef}
这定义\reserved@b
为\@gobble
并导致
\@yargd@f{2}\xyz{ab#1cd#2ef}
回想一下 的定义\@yarg@def
:
\long\def\@yargd@f#1#2{%
\def\reserved@a##1#1##2##{%
\expandafter\def\expandafter#2\reserved@b##1#1}%
\l@ngrel@x\reserved@a0##1##2##3##4##5##6##7##8##9###1%
}
参数是#1=2
和#2=\xyz
,所以我们得到(双精度##
在这里简化为单精度#
)
\def\reserved@a#12#2#{\expandafter\def\expandafter\xyz\reserved@b#12}
\l@ngrel@x\reserved@a0#1#2#3#4#5#6#7#8#9##1{ab#1cd#2ef}
第一个参数以\reserved@a
分隔2
,第二个参数以{
分隔。这是 TeX 的一个特殊功能:最后一个参数可以用替换文本的左括号分隔。
因此,在我们的例子中,第一个参数是0#1#
。由于\l@ngrel@x
,\long
它会触发 的扩展,\reserved@a
并带有以下参数:
\long\expandafter\def\expandafter\xyz\reserved@b0#1#2{ab#1cd#2ef}
因为\reserved@b
是\@gobble
,我们最终得到
\long\def\xyz{ab#1cd#2ef}