![防止活动字符在宏参数中扩展](https://linux22.com/image/463215/%E9%98%B2%E6%AD%A2%E6%B4%BB%E5%8A%A8%E5%AD%97%E7%AC%A6%E5%9C%A8%E5%AE%8F%E5%8F%82%E6%95%B0%E4%B8%AD%E6%89%A9%E5%B1%95.png)
目标:写入文件时未扩展活动字符
平均能量损失
\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 仅用于演示目的。)
该示例将 重新定义*
为 ,并将其扩展为!!
。这有效。
前言将 的原始定义存储到foo
中foo@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
。请将其删除。