防止活动字符在宏参数中扩展

防止活动字符在宏参数中扩展

目标:写入文件时未扩展活动字符

平均能量损失

\documentclass{article}

\makeatletter
% == Package code -- untouchable
\def\foo{\@ifnextchar[{\@foo}{\@foo[x]}}
\DeclareRobustCommand\fooaux[1]{\message{#1}}

\def\@foo[#1]{%
  \@foo@delegate{#1}}

\def\@foo@delegate#1{
  (#1)
  \@foo@write
}

\def\@foo@write#1{%
  \protected@write\@mainaux{}{\fooaux{#1}}
}

% == User code -- can manipulate at will

\let\foo@orig\foo
\def\foo@prep{\begingroup
% 1st attempt -- builds but active char expanded in AUX
  \catcode`\*=12%
% 2nd attempt -- error: ! Missing control sequence inserted. l.32 \foo@prep
%  \catcode`\*=13%
%  \def\@dettac{\char46}%
%  \let*=\@dettac
  \global\def\foo{\@ifnextchar[{\foo@ii}{\foo@ii[]}}%
  \global\def\foo@ii[##1]##2{%
    \foo@orig[##1]{##2}%
% 3rd attempt -- error: ! File ended while scanning definition of \@foo@arg@ii.
%     \edef\@foo@arg@ii{\scantokens{##2}}%
%     \foo@orig[##1]{\@foo@arg@ii}%
% 4th attempt -- ! Use of \foo@ii doesn't match its definition.
%   \edef\@foo@arg@ii{
%     \toks0={##2}
%     \immediate\openout14=foo.tex
%     \immediate\write14{\the\toks0}
%     \immediate\closeout14
%     \input foo.tex
%   }%
%   \foo@orig[##1]{\@foo@arg@ii}%
  }%
\endgroup
}

\foo@prep

\def\@catted{
  !!
}
\catcode`\*=13
\let*=\@catted

\makeatother

\begin{document}

Works: *

Work \foo{normal} \& \foo[ok]{works}

Do not \foo{five*four} \& \foo[is expanded in aux]{x*x}

\end{document}

标记的代码Package code来自第三方,无法更改。我只能更改User code

该包定义\foo[#1]{#2}最终写入某个文件的内容。(此处的 AUX 仅用于演示目的。)

该示例将 重新定义*为 ,并将其扩展为!!。这有效。

前言将 的原始定义存储到foofoo@orig,并尝试巧妙地重建foo,使用外部宏来提供对 12(其他)foo@prep的适当的 catcode 重新定义并在此上下文中重新定义。*foo

MWE 包含三次尝试。

尝试 1

将 catcode 重新定义为*12,并重新定义foo宏,传递未改变的参数。这不会产生错误,但会扩展,*就好像它仍然处于活动状态一样。

AUX 文件中的实际行数

\fooaux  {normal }
\fooaux  {works }
\fooaux  {five !! four }
\fooaux  {x !! x }

AUX 文件中的预期行

\fooaux  {normal }
\fooaux  {works }
\fooaux  {five * four }
\fooaux  {x * x }

第二次尝试

此尝试会留下*一个活动字符,并尝试使用 将其重新定义为原始字符char42。但是,这会产生错误

! Missing control sequence inserted.
l.33 \foo@prep

第三次尝试

然后我通过将宏参数设置为表示其参数的扩展的、正确的 catcoded 变体来尝试 eTeX\scantokens命令。\edef\@foo@arg@ii

但这也会产生错误

! Extra \endgroup.
l.33 \endgroup

我无法理解这一点。

[编辑修复语法错误并添加尝试 4]

第四次尝试

使用 token 列表代替\scantokens。这个产生

! Use of \foo@ii doesn't match its definition.
\@ifnextchar ... \reserved@d =#1\def \reserved@a {
                                                  #2}\def \reserved@b {#3}\f...
l.63 Work \foo{normal}
                       \& \foo[ok]{works}

有趣的是,这与我在真实文档中遇到的错误最为相似。

有人可以指导我如何“停用”作为宏参数传递的活动字符吗?

答案1

几年来,LaTeX 一直使用 e-TeX 扩展来运行 TeX,并且仅非常旧发行版不知道\protected

\documentclass{article}

\makeatletter
% == Package code -- untouchable
\def\foo{\@ifnextchar[{\@foo}{\@foo[x]}}
\DeclareRobustCommand\fooaux[1]{\message{#1}}

\def\@foo[#1]{%
  \@foo@delegate{#1}}

\def\@foo@delegate#1{
  (#1)
  \@foo@write
}

\def\@foo@write#1{%
  \protected@write\@mainaux{}{\fooaux{#1}}
}

% == User code -- can manipulate at will

\protected\def\@catted{
  !!
}
\catcode`\*=13
\let*=\@catted

\makeatother

\begin{document}

Works: *

Work \foo{normal} \& \foo[ok]{works}

Do not \foo{five*four} \& \foo[is expanded in aux]{x*x}

\end{document}

在此处输入图片描述

.aux文件内容

\relax
\fooaux  {normal}
\fooaux  {works}
\fooaux  {five*four}
\fooaux  {x*x}
\gdef \@abspage@last{1}

注意:我在其他地方的定义中留下了未受保护的结束行\@catted。请将其删除。

相关内容