在文档中
\catcode32=13\def {o} \bye
TeX 如何知道将后面的空格标记\def
为活动字符?
细节:正确的结果是o
。首先,我们将空格(ASCII 32)设置为活动字符(类别 13);然后,我们将它们定义为打印o
;然后,我们添加一个空格,打印o
,然后完成。
解析器如何识别文字数字的结尾13
?文字数字不能包含不可扩展的控制序列,例如\def
。但它能包含一个可扩展的控制序列,如\if
,我们在读完并查找之前无法知道这是哪一个。因此,直到我们解析完 token 后\def
,我们才知道 的结尾在哪里13
。
这里有点棘手。多字符控制序列会占用后面的空间。因此,在\catcode
运行赋值之前,\def
会占用后面的空间。但要运行赋值\catcode
,我们必须标记到标记的末尾\def
。
那么,TeX 如何知道不要占用该空间?
答案1
确实,如果令牌处理器创建了一个令牌并将其发送到扩展处理器,那么一旦分配的 catcode 就无法更改(有几个例外\string
,如\detokenize
等,但这超出了我们当前的重点)。
但是你的例子
\catcode32=13\def {o} \bye
不符合该句子的第二部分:后面的空格\def
在标记处理器级别等待处理。它不会被发送到扩展处理器。标记处理器\def
两次识别后面的空格。首先,当\def
构造控制序列时(此时空格具有类别代码 10)。然后标记处理器切换到“跳过空格”状态,它将发送\def
给扩展处理器,并保留空格字符以供进一步处理。扩展处理器无法扩展\def
,因此它告诉主处理器数字读取已完成,主处理器完成\catcode
分配并运行\def
。它在此处停用扩展处理器并要求标记处理器移动下一个标记。标记处理器获取等待空间并使用当前类别代码 13 从中创建一个标记。其“跳过空格”状态已完成,因为创建的标记不是类别为 10 的空格。它将标记空间/13 发送到主处理器。
从中得到的主要教训是:令牌处理器直到被发送到下一级处理(扩展处理器、主处理器)时才肯定会创建字符/catcode 对。
答案2
\catcode32=13\def {o} \bye
- 在收集形成第二个 TeX-⟨ 的 token 的过程中数字⟩-
\catcode
分配的数量,即 TeX-⟨数字⟩-数量被发现是⟨整型常数⟩ 由 ⟨ 序列组成数字以 ⟨ 开头的 ⟩-token数字⟩-token和 。112
312
- 作为⟨数字⟩-token被视为 ⟨ 的一个组成部分
312
整型常数⟩ 因此不适合终止收集属于 ⟨整型常数⟩,如果可扩展,则将更多的代币进行代币化和扩展,直到找到适合终止收集属于⟨的代币的过程的代币整型常数⟩ 不被视为 ⟨整型常数⟩. - 因此,在形成更多可能属于第二个 TeX-⟨ 的标记的过程中数字⟩-catcode 分配的数量,TeX 收集表示下一个标记的字符。
- 因此,TeX 遇到类别代码为 0(转义符)的字符
\
,并意识到要收集表示控制序列标记的字符。意识到这一点后,TeX 遇到该字符d
,由于该字符d
的类别代码为 11(字母),因此意识到要收集表示控制字标记的字符。 - 因此,TeX 还会收集具有类别代码 11(字母)的字符
e
和字符。f
- 然后 TeX 遇到空格字符,此时该字符的类别代码为 10(空格)。在标记化时,类别代码不为 11(字母)的字符不被视为控制字标记名称的组成部分。因此,空格字符终止了收集表示控制字标记的字符的过程,TeX 将控制字标记附加
\def
到标记流中,并将读取装置切换到状态 S(跳过空格)。
该代币\def
不被视为 ⟨整型常数⟩,因此收集属于⟨的代币的过程整型常数⟩/收集属于第二个 TeX 的标记的过程-⟨数字⟩-catcode-assignment 的数量终止,并且 catcode-assignment 已执行。
因此,当处理空格字符(终止收集\def
-token 的字符)时,空格字符的类别代码为 13(活动)。由于状态 S(跳过空格)仅影响类别代码 10(空格)的字符,因此空格字符被标记为活动字符标记。
如果你想熟悉 TeX 中标记化/处理 .tex 输入行的单个字符的阶段如何与其他处理阶段(扩展阶段/执行分配阶段)交织在一起,请尝试弄清楚为什么
\catcode`\.=0\def.macro{replacement}\macro\bye
锻炼的同时
\catcode`\.=0.def.macro{replacement}\macro\bye
.macro
产生关于/\macro
未定义的错误消息。
答案3
事实上,这很简单。
该
\catcode
标记本身就告诉 TeX 要执行类别代码分配;在这种情况下,TeX 会寻找一个 8 位 ⟨number⟩(对于 XeTeX 或 LuaTeX 来说,则是 21 位);
找到之后,TeX 会寻找 ⟨可选等号⟩ (
=
如果同时存在 ,则吞噬 后的空格);TeX 寻找一个 4 位数字。
如何指定 ⟨number⟩ 太长了,无法描述。 在您的例子中,两个必需的 ⟨number⟩ 都是显式常量,并且第 10 类空间将在数字序列后被吞噬。
还需要记住的是,读取了整行输入,并且\endlinechar
在删除尾随空格后将记录末尾替换为,但字符尚未被标记:TeX 仅在需要时才对输入进行标记。
因此以下代码行是完全等效的:
\catcode32=13\def {o} \bye
\catcode32 =13\def {o} \bye
\catcode32= 13\def {o} \bye
\catcode32=13 \def {o} \bye
\catcode32 = 13\def {o} \bye
\catcode32 =13 \def {o} \bye
\catcode32 = 13 \def {o} \bye
事实上,输入
{\catcode32=13\def {o} }\par
{\catcode32 =13\def {o} }\par
{\catcode32= 13\def {o} }\par
{\catcode32=13 \def {o} }\par
{\catcode32 = 13\def {o} }\par
{\catcode32 =13 \def {o} }\par
{\catcode32 = 13 \def {o} }\par
\bye
产生七个 o。您的情况是第一个,\def
不能解释为数字:在分配完成后,将标记放在一边重新读取。
让我们检查一下
\catcode32=13 \def {o} \bye
您可能想知道的就是这些(另外两个在 13 后面有一个空格的字符类似)。当 TeX 扫描完 后=
,它会继续寻找一个 4 位 ⟨number⟩。它找到了1
,所以它知道后面跟着一个显式常量。接下来是3
一个空格。尚未完成类别代码分配,因此此空格被赋予类别代码 10 并终止数字查找。现在 TeX 知道要分配什么类别代码,它会执行分配并丢弃后面的类别 10 字符。
也许你可以问会发生什么
\catcode32=13 \def {o} \bye
完全一样,因为 TeX 在扫描完一个分类码为 10 的字符后,就进入了“跳过空格”的状态,所以后面的分类码为 10 的字符就被忽略了。这个跳过空格的动作发生在分类码赋值之前,因为 TeX 想要“规范化”输入流。
练习:预测输出
\catcode32=13\def {o} \ \bye
(反斜杠后有两个空格)。
答案4
通过尝试诸如
\catcode`Z=13Z
和
\def\defZ{W}\catcode`Z=13\defZ{XY}
我觉得在尝试终止 catcode 分配时,TeX 会使用当前预先存在的 catcode 方案进行扫描。因此,它将决定\def<space>
终止\def
分配,然后恢复其扫描和 catcode 分配(与\futurelet
冻结 catcode 的情况不同),因此空间现在处于活动状态。
这不是一个权威的答案,因为不是基于实际的 TeX 源......
下面的例子
{\catcode`Z=13 \gdefZ{WWW}}\catcode`Z=13ZZ
打印出 ZWWW 表明至少在这里 catcode 11 Z(13 之后的第一个)没有被重新解释为 catcode13。
这必须添加到我之前的胡言乱语中(额外的胡言乱语是一些 catcode 可能会被冻结;在 OP 中,虽然它距离较远,但让我们等待正确的答案)。