makeatletter/makeatother 机制会导致解析问题吗?

makeatletter/makeatother 机制会导致解析问题吗?

\makeatletter使用/ 的原因\makeatother已在很多地方讨论过,例如在这个问题中

但是我认为,这种玩弄“@”字符的技巧本身就是一种黑客行为。临时重新定义 @ 的 catcode 不会导致问题吗?这可能不是实际最常见的使用场景,但如果一段由\makeatletter/包含的代码\makeatother使用宏\somemacro,并且我们习惯于将此宏应用于某些没有括号的参数,如\somemacro@,这不会破坏代码吗?此外,考虑到这种可能性,是否存在可能发生此类破坏的非人为用例?

(是的,这个问题可能属于“理论”领域,但熟悉解析问题和编译使我希望看到对此的讨论/记录。)

答案1

当你想直接地使用名称中带有 的宏@(从现在开始为 at-macro),那么您必须确保 被@视为字母,即调用必须在\makeatletter声明之后。

然而,在这种情况下,不需要使用以 at-macros 定义的普通宏。让我们看一个例子。

LaTeX内核定义\item如下:

\def\item{%
  \@inmatherr\item
  \@ifnextchar [ {\@item}{\@noitemargtrue \@item[\@itemlabel]}%
}

并且当 有效时它也会这样做\makeatletter。当\def执行时,TeX 会将\item和 的替换文本存储在内存中,但其形式与构成所用控制序列名称的实际字符无关。因此,当\item出现在文档中时(通常当有效时),其扩展包含 at-macros 的事实对于扩展过程而言无关紧要:TeX 从其内存中检索内部形式\makeatother的含义,当第一个控制序列必须扩展时也会发生同样的情况。\item\@inmatherr

相反,如果您需要修改的定义\item以便在开头添加一些内容,则必须正确进行:

\makeatletter
\def\item{%
  \typeout{Doh! An item!}%
  \@inmatherr\item
  \@ifnextchar [ {\@item}{\@noitemargtrue \@item[\@itemlabel]}%
}
\makeatother

因为您指的是定义主体中的 at-macros。如果没有\makeatletter,该行将\@inmatherr\item被解析(我使用 • 将一个标记与另一个标记分开)

\@•i•n•m•a•t•h•e•r•r•\item

也就是说,有 11 个标记,而不是 2 个!这是因为在正常情况下@不是字母:当 TeX 因为找到反斜杠而寻找命令名称时,任何非字母都会立即停止扫描,命令名称将由非字母。

什么是字母?类别代码为 11 的任何字符扫描正在进行时。但是,我再说一遍,一旦扫描完成,控制序列就会以独立于类别代码的内部格式进入 TeX。

通过查看您的示例\somemacro@,可以“轻松”区分这两种情况:

\makeatletter
\somemacro@ % <- ONE token
\makeatother
\somemacro@ % <- TWO tokens

因此,如果您在文档的正常位置定义了\somemacro@和 (作为 at-macro) ,则组合将扩展,而 at-macro 不会扩展。实际上,控制序列名称的扫描将因 而停止,而 不是文档中的字母(当然,如果您还没有这样做)。\somemacro\somemacro@\somemacro@\makeatletter

以下情况可能会引起疑问:

\makeatletter
\def\somemacro@#1{Do something smart with #1}
\def\somemacro#1{\somemacro@{(#1)}}
\makeatother

和输入(在正常情况下,即\makeatother

\somemacro@

现在这些标记:\somemacro•@,因此@将是参数\somemacro,最终结果将是打印

使用 (@) 做一些聪明的事情

需要一段时间来消化这些技巧;但是一旦人们意识到 TeX 通过读取输入文件来处理它所形成的标记,并在将其转换为内部格式(这对于用户来说无关紧要)后对其进行处理,事情就会开始变得更加顺利。

相关内容