当 LaTeX 在 dtx 文件上运行时究竟会发生什么?

当 LaTeX 在 dtx 文件上运行时究竟会发生什么?

我对docstrip/ 的ltxdoc运行方式感到困惑。似乎在调用这么简单的dtx文件时,LaTeX 会运行两次。

% \iffalse
%<*installation>
\expandafter\begingroup
\input docstrip.tex
\keepsilent
\askforoverwritefalse
\nopreamble
\nopostamble
\generate{\file{\jobname.cls}{\from{\jobname.dtx}{latex}}}
\expandafter\endgroup
%</installation>
%<*documentation>
\ProvidesFile{\jobname.dtx}
\documentclass{ltxdoc}
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</documentation>
%<*latex>
% \fi
%    \begin{macrocode}
\newcommand{\mystuff}{mystuff}
%    \end{macrocode}
% \iffalse
%</latex> 
% \fi

我很容易理解第一遍:按照命令(块)的要求docstrip读取并生成文件。.cls\generateinstallation

但第二遍似乎已经完成了第一遍团体(从\begingroup\endgroup)被忽略,文件的其余部分(块documentation)被用作普通 LaTeX 代码,从而产生预期的.pdf(或.dvi文件)。

如果删除\expandafter\begingroup\expandafter\endgroup行,则仅完成第一遍并会引发错误。

我读过docstrip文档(和代码,但肯定不够认真),但没有发现任何关于这一点的暗示双倍的执行。

因此,我正在寻找以下子问题的答案:

  • 我说得对吗(两次传球)?
  • 我是否遗漏了docstrip文档中的某些内容(如果是,在哪里)?
  • 代码读两次还是一次?
  • 为什么我们需要第一个组存在才能使 LaTeX 生成两个预期的文件?

答案1

了解 TeX 读取.dtx文件时发生的情况的最佳方法是慢慢处理并跟踪行。我将使用您的示例并进行一些小修改(以修复看似错误的内容并添加一些有用的说明。)我假设我们正在运行,latex <myfile>.dtx因为在这种情况下,这个特定的示例是“自我提取”。我还将对行进行分组,原因将变得清晰。虽然我们将看到只有一次运行,但对同一个文件进行了多次传递:这会变得复杂,所以我将尝试分步进行。

'主要' 通行证

我们从以下行开始

% \iffalse

这暂时只是一条评论,但稍后会变得重要。

接下来我们得到

%<*installation>
\begingroup
\input docstrip.tex
\keepsilent
\askforoverwritefalse
\nopreamble
\nopostamble
\generate{\file{\jobname.cls}{\from{\jobname.dtx}{latex}}}
\endgroup
%</installation>

在此阶段,“保护”行%<*installation>%</installation>是注释,将被忽略。因此,TeX 打开一个组,然后输入程序 DocStrip。这是在组中完成的,因此 DocStrip 的内容不会影响后续内容。

然后,DocStrip 在到达该行之前进行一些设置\generate。这是一条再次读取\jobname.dtx(当前文件)但进行其他一些处理以创建 的指令\jobname.cls。现在就发生了这种情况,但为了暂时理解这个过程,我将把它视为一个已经创建的黑匣子\jobname.cls,然后在 DocStrip 过程部分中返回。

一旦小组关闭,我们的命令含义就恢复正常了。移动一个我们有

%<*documentation>
\ProvidesFile{\jobname.dtx}
\documentclass{ltxdoc}
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</documentation>

这就是所谓的“驱动程序”部分(多年前.dtx它曾以文件形式提供)。除了行之外,这部分看起来像一个普通的 LaTeX 文件。这里发生的事情是,LaTeX 为正常排版设置,然后在排版运行中再次读取。同样,这实际上发生在“这里”,但我会单独讨论它。.drv\DocInput\jobname.dtx

正如我们现在已经通过的\end{document}

%<*latex>
% \fi
% Some text about this macro.
%    \begin{macrocode}
\newcommand{\mystuff}{mystuff}
%    \end{macrocode}
% \iffalse
%</latex> 
% \fi

永远不会作为“主要”通道的一部分被读取,所以我们现在不必担心它们。

最终结果是一个类文件和一个 PDF:它们来自我将在下面描述的两个次要“过程”。

DocStrip 通行证

DocStrip 读取文件以查找保护,然后将任何非注释行复制到它被要求的任何文件中。在我们的 DocStrip 运行中,我们\jobname.cls使用保护名称创建一个文件latex。让我们看看这个过程如何读取.dtx

我们再次以一个不是保护的注释行开始,因此它被完全忽略了

% \iffalse

下一行是一个保护(以 开始%<):如果你仔细阅读 DocStrip,你会看到%<*表示这个保护一直适用直到匹配</。这里的保护名称是installation:这不是我们想要的,所以所有这些行都被跳过了。(请注意,保护行是特殊的注释,因此 DocStrip 并不完全忽略注释行。)

%<*installation>
\begingroup
\input docstrip.tex
\keepsilent
\askforoverwritefalse
\nopreamble
\nopostamble
\generate{\file{\jobname.cls}{\from{\jobname.dtx}{latex}}}
\endgroup
%</installation>

现在我们找到了第二个保护块,这次称为documentation。这仍然不是我们想要的,所以它再次被忽略。

%<*documentation>
\ProvidesFile{\jobname.dtx}
\documentclass{ltxdoc}
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</documentation>

最后我们找到了我们要找的守卫。这里的任何非注释行都被复制到\jobname.cls:这意味着只有一个读取\newcommand{\mystuff}{mystuff}。您可能想知道这里的注释行:它们将在排版部分中脱颖而出。

%<*latex>
% \fi
% Some text about this macro.
%    \begin{macrocode}
\newcommand{\mystuff}{mystuff}
%    \end{macrocode}
% \iffalse
%</latex> 
% \fi

排版通行证

在行操作期间,\DocInput{\jobname.dtx}我们正在寻找要排版的文档主体。LaTeX 正在再次读取我们的文件,但这次至关重要的%是第一列中的不是注释,则完全被忽略。因此,需要先忽略注释%(如果存在),然后读取所有行。

在这种制度下,第一行

% \iffalse

不是注释,而是\iffalse条件。这意味着“跳过所有内容,直到匹配\fi,因此以下几行

%<*installation>
\expandafter\begingroup
\input docstrip.tex
\keepsilent
\askforoverwritefalse
\nopreamble
\nopostamble
\generate{\file{\jobname.cls}{\from{\jobname.dtx}{latex}}}
\expandafter\endgroup
%</installation>
%<*documentation>
\ProvidesFile{\jobname.dtx}
\documentclass{ltxdoc}
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</documentation>
%<*latex>

全部跳过,直到匹配

% \fi

行。结果是没有提取任何内容,否则驱动程序将被排版。

然后我们得到

% Some text about this macro.
%    \begin{macrocode}
\newcommand{\mystuff}{mystuff}
%    \end{macrocode}

再次强调,%这里没有注释,所以我们获取了类macrocode提供的环境ltxdoc。这会将内容(代码行)排版为代码并执行索引等操作。我还在这里添加了一行普通文本:这将在环境之前排版macrocode

最后我们得到

% \iffalse
%</latex> 
% \fi

这里的警戒通常会被排版,但作者跳过了这一点,用条件语句包围了它\iffalse。(我可能会排版警戒,所以我会把它们放在里面环境macrocode

结论

这里只有一次 TeX 运行,但同一个源被读取了三次,所以最简单的方法是将其视为三次传递。如果你这样做,你可以相对容易地弄清楚发生了什么。(也许值得注意的是,“经典”.dtx文件会将该部分installation作为单独的.ins文件,而不需要组,而且正如我所指出的,非常旧的 DTX 文件也将驱动程序/排版部分作为单独的文件。)

相关内容