当查看filecontents
文件中的环境latex.ltx
(或中的类似版本filecontents.sty
)时,我发现了这段我无法理解的代码:
\edef\E{\@backslashchar end\string{\@currenvir\string}}%
\edef\reserved@b{%
\def\noexpand\reserved@b%
####1\E####2\E####3\relax}%
\reserved@b{%
\ifx\relax##3\relax%
\immediate\write\reserved@c{##1}%
\else%
\edef^^M{\noexpand\end{\@currenvir}}%
\ifx\relax##1\relax%
\else%
\@latex@warning{Writing text `##1' before %
\string\end{\@currenvir}\MessageBreak as last line of #1}%
\immediate\write\reserved@c{##1}%
\fi%
\ifx\relax##2\relax%
\else%
\@latex@warning{%
Ignoring text `##2' after \string\end{\@currenvir}}%
\fi%
\fi%
^^M}%
\catcode`\^^L\active%
\let\L\@undefined%
\def^^L{\expandafter\ifx\csname L\endcsname\relax\fi ^^J^^J}%
\catcode`\^^I\active%
\let\I\@undefined%
\def^^I{\expandafter\ifx\csname I\endcsname\relax\fi\space}%
\catcode`\^^M\active%
\edef^^M##1^^M{%
\noexpand\reserved@b##1\E\E\relax}}%
此环境用于在文件中写入一些文本。将所有 catcode 转换为 12 个字符后,使用命令将文本写入文件中\reserved@b
。我对这个编码有很多困扰:
为什么有一些递归编码?
\edef\reserved@b{% \def\noexpand\reserved@b% ####1\E####2\E####3\relax}%
参数分隔符的作用是
\E
什么\end{filecontents}
- 为什么行尾 (
^^L
) 被转换成\end{filecontents}
任何有关此代码中使用的精细技巧的帮助都将不胜感激。
答案1
按顺序回答这三个问题:
从历史上看,可用的宏名称数量非常有限。因此,反复使用相同的名称很有用。在 LaTeX 内核中,\reserved@a
等仅供团队使用\@tempa
(请参阅哪些宏应该用作“临时空间”?和@tempdima,reserved@a,@tempcnta,还有什么?了解详细信息)。片段
\edef\reserved@b{%
\def\noexpand\reserved@b%
####1\E####2\E####3\relax}%
用作\reserved@b
一次性宏来\reserved@b
以不同的方式定义:可以用不同的名称来完成,并且不是递归。这个想法是我们需要\edef
“某些东西”来正确设置\reserved@b
。
\E
这很好地引出了第二个问题。在上面,
\edef\E{\@backslashchar end\string{\@currenvir\string}}
因此,当它扩展时,我们会被\E
替换\end{<envname>}
。这用于创建\reserved@b
看起来像
> \reserved@b=macro:
->\def \reserved@b ####1\end{<envname>}####2\end{<envname>}####3\relax .
正如您所看到的,这\edef
意味着它\E
不再存在:它是另一个一次性的宏名称,可以使用其他东西来完成。
的奇怪定义^^L
是允许从扩展上下文中的设置生成警告消息。我们稍后会
\gdef\endfilecontents{|
\immediate\closeout\reserved@c
\def\T##1##2##3{|
\ifx##1\@undefined\else
\@latex@warning@no@line{##2 has been converted to Blank ##3e}|
\fi}|
\T\L{Form Feed}{Lin}|
\T\I{Tab}{Spac}|
\immediate\write\@unused{}}
这意味着如果\L
定义了,就会出现警告。但如何定义\L
?已经完成了
\def^^L{\expandafter\ifx\csname L\endcsname\relax\fi ^^J^^J}%
由于\csname
会将未定义的控制序列转换为\relax
,这让我们可以设置一个“标志”,表示即使换行发生在 中,也可以看到换行\write
。(我们在expl3
一般形式中有一个类似的技巧。)