在可扩展的令牌完整扩展,保留 catcodes,约瑟夫·赖特给出以下代码:
\long\def\fullyexpand#1{%
\csname donothing\fullyexpandauxi{#1}{}%
}
\long\def\fullyexpandauxi#1{%
\expandafter\fullyexpandauxii\romannumeral -`0#1\fullyexpandend
}
\long\def\fullyexpandauxii#1#2\fullyexpandend#3{%
\ifx\donothing#2\donothing
\expandafter\fullyexpandend
\else
\expandafter\fullyexpandloop
\fi
{#1}{#2}{#3}%
}
\long\def\fullyexpandend#1#2#3{\endcsname#3#1}
\long\def\fullyexpandloop#1#2#3{%
\fullyexpandauxi{#2}{#3#1}%
}
\def\donothing{}
他说道:
我还要指出的是,上面的代码需要为空白(空或全空格)参数添加一些保护,因为目前在这些情况下事情会失败。
这确实没错,即使不完全符合语法。不幸的是,我的 TeX 还不够好,所以我不知道如何添加这样的保护。有人愿意吗?
(对于我的应用程序来说,任何能在 pdfTeX 中运行的东西都可以。)
答案1
\csname
此版本需要恰好两次扩展才能工作,因此通过使用两次应用来避免构造\romannumeral
。外部应用确保我们恰好需要两次扩展,内部应用进行扩展:
\long\def\fullyexpand#1{%
\romannumeral-`0%
\fullyexpandauxi{#1}{}%
}
\long\def\fullyexpandauxi#1{%
% The space in the following line is deliberate: it will always finish the
% \romannumeral before \fullyexpandauxii expands
\expandafter\fullyexpandauxii\romannumeral-`0#1 \fullyexpandend
}
\long\def\fullyexpandauxii#1{%
\ifx\fullyexpandend#1%
\expandafter\fullyexpandend
\else
\expandafter\fullyexpandauxiii
\fi
{#1}%
}
\long\def\fullyexpandauxiii#1#2\fullyexpandend#3{%
\expandafter\fullyexpandauxii\romannumeral-`0#2 \fullyexpandend{#3#1}%
}
% Here, #1 will be "\fullyexpandend", as we have reached the end of the loop
\long\def\fullyexpandend#1#2{ #2}
先前版本对完全空白的参数进行了测试,但是如果参数不为空而是扩展为以下内容,则测试会失败:
\long\def\fullyexpand#1{%
\romannumeral-`0%
\expandafter\ifx\expandafter\relax\detokenize\expandafter
{\gobble#1 ?}\relax
\expandafter\fullyexpandblank
\else
\expandafter\fullyexpandauxi
\fi
{#1}{}%
}
\long\def\gobble#1{}
\long\def\fullyexpandauxi#1{%
\expandafter\fullyexpandauxii\romannumeral -`0#1\fullyexpandend
}
\long\def\fullyexpandauxii#1#2\fullyexpandend#3{%
\expandafter\ifx\expandafter\relax\detokenize{#2}\relax
\expandafter\fullyexpandend
\else
\expandafter\fullyexpandloop
\fi
{#1}{#2}{#3}%
}
\long\def\fullyexpandend#1#2#3{ #3#1}
\long\def\fullyexpandloop#1#2#3{%
\fullyexpandauxi{#2}{#3#1}%
}
\long\def\fullyexpandblank#1#2{ }