EDIT2: the previous code was broken when given conditional text as an input. I think this should be better. Also, the explanations were confusing. Now, for all the explanations below I \let\ea\expandafter. But not in the code.
TOC: Explanations ("Two user commands", "How it works"). Code ("Implementation", "Tests").
Explanations
Two user commands
For n>0, \MultiExpand{n}\macro gives the n-th expansion of \macro after two steps of expansion.
For n>0, \MultiExpandAfter{10}\macroA\MultiExpandAfter{4}\macroB\macroC expands \macroC 4 times before expanding \macroB 8=10-2 times, and finally \macroA. It also requires two steps.
The first expansion of \MultiExpand yields a very useful sequence of tokens: expanding \unless\ifcsname\multiexpand\fi{n} once expands the following token n times. The same exists for expanding after.
These are especially useful when we want to expand several times a very specific token which is buried behind many others. Example: after \def\macroA{\macroY\macroZ}, the code
\MultiExpand{5}\expandafter\macroA\expandafter\macroB%
\unless\ifcsname\multiexpandafter\fi{4}\macroC\macroD
will expand \macroD 4 times, then will expand \macroA 4=5-1 additional times. Also, these tokens can be used to force expansion of an expandable macro, more or less like TH. was asking for: just prepend it the macro with the relevant few tokens.
Note: if you really need \MultiExpand{0}, just do \empty\empty.
How it works
\unless\ifcsname expands tokens fully until it reaches \endcsname. If the control sequence thus built exists, then it jumps to the matching \fi and eats it. All this happens in one step: try \ea\def\ea\foo\ea{\unless\ifcsname let\endcsname \fi}. Since \let exists, \foo becomes empty. Try removing \unless. Then the conditional becomes true, and the \fi remains: the full expansion would require two steps. We will always arrange for this conditional to be false, to get rid of the \fi. Since it is much easier to ensure that a command exists than not, we use \unless\ifcsname ifcsname\endcsname\fi.
Now, as I said before, \ifcsname expands tokens. The trick is to put the various tokens that we want to expand between the \ifcsname and the matching \endcsname. The simplest example is \unless\ifcsname\endifcsname:. In one step, it expands to the empty token list, and additionally expands its argument once. If you think about it, (almost) everything is just as if these three tokens were not there.
This is in fact how the construction ends. Let us quickly look at the definition of \multiexpand:n, whose argument k is the number of expansion left to do. If k=1, we end the \ifcsname as described in the paragraph above, throwing away two last lines in braces (I hide this fact at the end of the macro name \endifcsname:n). Otherwise, we keep the two lines in braces: they do one step of expansion (\numexpr#1-1\expandafter), and leave \multiexpand:n{k-1} on the stream. It will be expanded, since we are still inside the construction of the cs name for \ifcsname.
The code
Implementation
% I follow the LaTeX3 convention of finishing each macro name by its
% argument specification, e.g. ":nnnN" for three braced arguments and
% one single token. For this, I need "_" and ":" to be letters.
\catcode`\:=11\relax%
\catcode`\_=11\relax%
\def\use:n#1{#1}%
\def\endifcsname:{ifcsname\expandafter\endcsname\expandafter\fi}%
\def\endifcsname:n#1{\endifcsname:}%
\def\endifcsnameafter:n#1{\endifcsname:\expandafter}%
%
% The user commands are \MultiExpand and \MultiExpandAfter.
\def\MultiExpand{\unless\ifcsname\multiexpand\fi}%
\def\multiexpand\fi{\multiexpand:n}%
\def\multiexpand:n#1{%
\ifnum#1<2 \expandafter \endifcsname:n%
\else \expandafter \use:n%
\fi%
{\expandafter \multiexpand:n \expandafter {%
\number\numexpr#1-1\expandafter}}%
}%
% Almost identical definitions for expanding after...
\def\MultiExpandAfter{\unless\ifcsname\multiexpandafter\fi}%
\def\multiexpandafter\fi{\multiexpandafter:n}%
\def\multiexpandafter:n#1{%
\ifnum#1<2 \expandafter\endifcsnameafter:n%
\else \expandafter \use:n%
\fi%
{\expandafter \multiexpandafter:n \expandafter {%
\number\numexpr#1-1\expandafter}\expandafter}%
}%
\catcode`\_=8\relax%
\catcode`\:=12\relax%
Tests
There is no limit on the number of expansions.
% ======= Tests =====
\long\gdef\expandonce#1{% redefines #1 as #1 expanded once.
\long\edef#1{\unexpanded\expandafter\expandafter\expandafter{#1}}}
% Commands that expand to each other, to count how many times things
% were expanded.
\def\0{\1} \def\1{\2} \def\2{\3} \def\3{\4} \def\4{\5}
\def\5{\6} \def\6{\7} \def\7{\8} \def\8{\9} \def\9{\0x}
% Test \MultiExpand, after two expansions,
% we get the 2011th expansion of \0.
\def\:{\MultiExpand{2011}\0} \show\:
\expandonce\: \show\:
\expandonce\: \show\:
% Test \MultiExpandAfter, two expansions to get the specified expansion.
% The results also show that the expansion happens in the expected order.
\def\:{%
\MultiExpandAfter{27}\0% note that we get \5xx (25=27-2).
\MultiExpandAfter{14}\0% note that we get \2x (12=14-2).
\MultiExpandAfter{38}\0\0}% note that we get \8xxx... no "-2".
\show\:
\expandonce\: \show\:
\expandonce\: \show\:
For comparison, the "Hello World!" example
\def\a#1.{#1}
\def\b#1:{#1.}
\def\c#1,{#1:}
\def\d{Hello world!,}
\MultiExpandAfter3\a
\MultiExpandAfter3\b
\MultiExpandAfter1\c
\d
now uses 32 \expandafters, slightly better than Hendrik's solution (however, we should count the overhead with all the \csname and friends). The other example on which Hendrik was optimizing, namely, expanding seven tokens once each in the reverse order, takes 74 \expandafter:
\def\a{a} \def\b{b} \def\c{c} \def\d{d}
\def\e{e} \def\f{f} \def\g{g}
\begingroup\tracingall
\MultiExpandAfter3\a \MultiExpandAfter3\b
\MultiExpandAfter3\c \MultiExpandAfter3\d
\MultiExpandAfter3\e \MultiExpandAfter1\f\g
\endgroup
Some experimentation tells me that the number of \expandafter is roughly 5 times the sum of the arguments of the various \MultiExpandAfter.
\expandafteris a primitive, and is therefore executed, whereas and\superexpandafterwill be a macro and will itself need expansion. There are various approaches which need various numbers of expansions. Perhaps you might specify how many we're allowed? (I think something similar was discussed by the NTS while writing e-TeX, but as a potential primitive. Never happened, of course.) – Joseph Wright♦ Jan 1 '11 at 19:39\eafor\expandafterin this comment.] The main problem is that in\ea\ea\ea \foo \ea\ea\ea \bar \baz, we can definitely not replace each\ea\ea\eaby a single macro separately. Maybe it would be possible to write something along the lines of\superexpandafter{permutation of [1,n]}that, when expanded (once? twice?), would yield the result of expanding the n following macros in a specific order. Then, we can hopefully use it in a sensible way, never preceeded by\expandafters. --- Of course, a primitive would be the nicest :). – Bruno Le Floch Jan 1 '11 at 21:33