尝试理解 TeX 解析维度的规则时,我发现了以下奇怪的行为:
\dimen0=1\noexpand\noexpand\noexpand\noexpand\noexpand\empty pt
是有效的,但是
\dimen0=1\noexpand\noexpand\noexpand\noexpand\noexpand\noexpand\empty pt
产量
! Illegal unit of measure (pt inserted).
<to be read again>
p
<to be read again>
t
<*> ...oexpand\noexpand\noexpand\noexpand\empty pt
?
是什么导致了这种差异?
答案1
这很难追踪。请注意,\noexpand
根据单位的不同,在不同数量的 之后,我们会收到错误,这意味着 TeX 根据单位进行不同数量的扩展。为了试验这一点,让我们将活动字符设置~
为\noexpand
,然后尝试各种分配,增加 的数量,直到 TeX 抱怨。以下是原始 TeX 引擎~
的最大数量:~
\let~\noexpand
\dimen0=1~~\dimen1
\dimen0=1~~~em
\dimen0=1~~~~ex
\dimen0=1~~~~~true~pt % (and others)
\dimen0=1~~~~~~pt
\dimen0=1~~~~~~~in
\dimen0=1~~~~~~~~pc
\dimen0=1~~~~~~~~~cm
\dimen0=1~~~~~~~~~~mm
\dimen0=1~~~~~~~~~~~bp
\dimen0=1~~~~~~~~~~~~dd
\dimen0=1~~~~~~~~~~~~~cc
\dimen0=1~~~~~~~~~~~~~~sp
\dimen0=1~~~~~~~~~~~~~~~ % crashes after reading the last ~.
显然,并非所有单位都生来平等。关键在于文件中tex.web
:TeX 用于扫描单位的代码复制如下。让我们来评论一下
\dimen0=1~~~~~~~pt
例如,这会导致错误(7 ~
)。TeX 读取数字1
,扩展第一个数字~
,暂时将第二个数字变为~
,\relax
从而停止扩展1
:数字已完成。以下是:
@<Scan units and set |cur_val| to $x\cdot(|cur_val|+f/2^{16})$...@>=
这本质上是一个“标题”。
if inf then @<Scan for \(f)\.{fil} units; |goto attach_fraction| if found@>;
尺寸分配不允许无限粘合,因此跳过此部分。
@<Scan for \(u)units that are internal dimensions;
|goto attach_sign| with |cur_val| set if found@>;
这将扫描诸如 之类的单元\dimen...
,扩展第二个单元~
,将下一个单元转换~
为\relax
(请注意,在我们的示例文件的第一行中,~
作用于\dimen
并且不执行任何操作,因此 TeX 会看到\dimen
)。然后 TeX 查找em
,然后查找ex
,“杀死”了另外两个~
。我们剩下 3 个。
if mu then @<Scan for \(m)\.{mu} units and |goto attach_fraction|@>;
已跳过:这不是一个跳过任务
if scan_keyword("true") then @<Adjust \(f)for the magnification ratio@>;
@.true@>
TeX 尝试扫描true
并再次失败,剩下 2 ~
。
if scan_keyword("pt") then goto attach_fraction; {the easy case}
@.pt@>
TeX 查找pt
,并进行扩展,但下一个~
将最后一个 变成~
,\relax
隐藏了pt
后面的 。又一次失败。
@<Scan for \(a)all other units and adjust |cur_val| and |f| accordingly;
|goto done| in the case of scaled points@>;
此处调用的代码逐个尝试各种单元:in
、pc
、cm
、mm
、bp
、dd
、cc
。sp
我们的最后一个~
对以下 不做任何操作p
,而 TeX 不断看到p
,并尝试将其与单元匹配。当扫描 时pc
,TeX 识别p
,但对 感到失望t
。最终,TeX 放弃了。请注意,在这种情况下,错误消息将 和 都显示p
为t
“需要再次读取”。如果再添加两个,TeX在尝试解析 时~
尚未到达,因此只有会“需要再次读取”。p
pc
p
attach_fraction: if cur_val>=@'40000 then arith_error:=true
else cur_val:=cur_val*unity+f;
done:
其余的代码我们这里不关心。
附注:由于 eTeX 比 TeX 多扫描几个单元,因此以下程序~
在 TeX 中会在 14 处崩溃,但在 eTeX 中会在 18 处崩溃:
\let~\noexpand
\dimen0=1~~~~~~~~~~~~~~~~~sp