这基本上是一个关于 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) 放置一个宏,其中\everypar
b) 吞噬整个(或者更确切地说:其余部分)段落,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
,从而打开,其中\vbox
将A
再次读取,触发一个新段落,因此调用\foo
…
在这种情况下,当尝试 500 级装箱时,程序会停止。同样的问题解释了您遇到相同错误的情况。