参数文本中的 \par(作为参数分隔符)和 \everypar 宏中的 \vbox{#1}

参数文本中的 \par(作为参数分隔符)和 \everypar 宏中的 \vbox{#1}

这基本上是一个关于 TeX 错误消息的问题。

当我尝试执行上述操作时,TeX 给出的神秘错误消息让我感到困惑。我试图隔离它,但只得到以下结果:

\def\foo#1\par{\vbox{#1}}
\def\bar#1\par{\hbox{#1}}

\let\macro\foo
\macro A paragraph consisting of text.

\let\macro\bar
\macro A paragraph consisting of text.

\everypar={\macro}
A paragraph consisting of text.

\let\macro\foo
A paragraph consisting of text.

\bye

TeX 都可以接受这些,除了最后一个例子,它给出了错误消息:

! Argument of \macro has an extra }.
<inserted text> 
            \par 
<to be read again> 
               }
l.16 

“l. 16”指向 之前的行\bye,即 TeX 遇到两个连续换行符的时刻(\par我假设这导致它插入一个标记)。

我知道\everypar插入水平列表已经开始,吞噬段落并不是一个明智而干净的想法它已经开始了(因此“段落的其余部分”可能是更好的描述)并在 vbox 中排版。但随后

\indent\vbox{This is some text.}

可以接受。当然,如果你在段落内的垂直框内排版整个段落,整个段落就会排版\hsize并整体向右移动\parindent,这看起来很糟糕。但这不是我的观点。

我想了解的是,为什么显然不可能 a) 放置一个宏,其中\everyparb) 吞噬整个(或者更确切地说:其余部分)段落,c) 将其放入 vbox,而所有这些事情显然单独都可以做到?特别是,如果我们只将 c) 更改为 hbox 而不是 vbox,错误消息就会消失。换句话说:为什么当}我们使用 vbox 时,TeX 会突然看到一个额外的内容?

当我刚刚输入所有这些内容时,我开始明白了:问题是否是由于在 vbox 中,TeX 启动了一个新的水平列表,\everypar再次插入了 的内容,从而引发了无限递归?在这种情况下,它与 \par 作为参数分隔符无关,而只是因为在 中\everypar不应开始新的段落。事实上,如果我说

\def\macro#1\par{\vbox{\noindent}#1}

(假设\macro仍如上\everypar所示),排版下一个段落时也会出现同样的错误。

现在我似乎明白了错误来自哪里,但错误消息是什么意思?为什么 TeX 会看到一个额外的}?为什么它在吞噬了参数之后才报告它\macro?如果我说

\everypar={\vbox{\noindent}}
A paragraph consisting of text.

我收到错误消息

! TeX capacity exceeded, sorry [semantic nest size=500].
<everypar> \vbox {\noindent 
                            }
[many similar lines left out]
...
l.17 A
       paragraph consisting of text.

从我上面似乎已经弄清楚的内容来看,这似乎很清楚。

根据 egreg 的出色解释进行编辑:

[至于上面给出的代码确实不会导致任何错误消息;它确实会导致错误消息——并说明了我遇到的问题——如果你在示例之间放置两行空行而不是一行。]

您将 \everypar={} 放入 vbox 内部的技巧很棒,因为它对于 vbox 而言是本地的,对吗?

然而,我仍然想知道为什么在某些例子中,TeX 会报告超出“嵌套大小”,我认为这是可以理解的,而在其他例子中(原因相同),everypar 中宏的参数有一个额外的 }。我知道 TeX 的错误消息是出了名的神秘,但无论如何,我想知道是否有可能理解它们。

我会尝试一下:

当 \everypar 设置为 {\macro} 后,TeX 遇到下一个段落时,它会执行并扩展 \macro,它会吞噬该段落(并丢弃末尾隐含的 \par),然后打开一个 vbox 来放置该段落。由于 vbox 中的段落文本开始了一个新段落,因此会再次执行 \everypar,再次调用 \macro,它再次吞噬该段落文本,但在任何 \par 标记之前遇到 vbox 的结束 }。因此,第二次调用 \macro 会触发此错误消息。

这就是全部内容吗?我实际上原本期望 vbox 中的结束 } 应该会导致隐式 \par。

答案1

第一个例子没有错误,除非我在\let\macro\foo和之间添加一个空行A

\def\foo#1\par{\vbox{#1}}
\def\bar#1\par{\hbox{#1}}

\let\macro\foo
\macro A paragraph consisting of text.

\let\macro\bar
\macro A paragraph consisting of text.

\everypar={\macro}
A paragraph consisting of text.

\let\macro\foo

A paragraph consisting of text.

\bye

为什么?在您的示例中,\par前面的内容\let\macro\foo已被吞噬\bar,因此没有开始新的段落。

现在,为什么会出现错误?原因相同!当\foo执行 (以 的名称执行\macro)时,\par前面的内容\bye会作为参数文本的一部分被吞噬,因此当A在 中重新扫描时\vbox,会启动一个段落并\foo再次展开,但缺少结尾\par。按回车键将启动无限循环。

这种情况不会发生,\hbox因为\everypar它内部没有执行。

“解决方案”:

\def\foo#1\par{\vbox{\everypar{}#1}}

当然,这将产生一个过满的框,因为段落开始所造成的缩进以及事实将会\vbox\hsize宽。


您无法通过添加\par以下内容来“解决”该问题\foo

\def\foo#1\par{\vbox{#1\par}}

因为这将导致

! TeX capacity exceeded, sorry [semantic nest size=500].
<to be read again> 
                   A
<argument> A

原因是再次A将触发一个新段落,因此调用\foo,从而打开,其中\vboxA再次读取,触发一个新段落,因此调用\foo

在这种情况下,当尝试 500 级装箱时,程序会停止。同样的问题解释了您遇到相同错误的情况。

相关内容