EDIT: At the end of this post, I include the relevant part of ULcase.sty so that this post is self contained.
In the code below I use ULcase. I think that the comments in the code should be enough to understand it.
The easiest is \expandallonce, although what do you want when a token takes an argument? (Currently, it will break.) We simply define a capitalization table which by default expands once, and we read through the tokens.
% Code based on the extended Upper- and Lower-casing code found
% in the ULcase package.
\input ULcase.sty\relax
% ============ Table |expandallonce|
% Just as uppercasing is changing "a->A", "b->B" etc, and applying
% |\donothing| to all other tokens, we define the table of case change
% |expandallonce| to expand the token once before outputting it. This
% will fail in case the token takes an argument (I don't know what the
% expected behaviour would be, anyways.)
%
\long\gdef\expandallonce#1{%
\UL_to_case:nn{expandallonce}{#1}}
% The default action is to output the given token expanded once.
\long\gdef\UL_table_expandallonce_default#1{%
\expandafter\UL_to_case_output:n\expandafter{#1}}
% Braces do nothing special, just as in the ULnil table.
\long\gdef\UL_table_expandallonce_braces#1#2{%
\expandafter\expandafter\expandafter\UL_to_case_output:n%
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\expandafter{\UL_to_case:nn{#1}{#2}}%
}%
}
% ===== Tests
\def\0{\1}\def\1{\2}\def\2{\3}\def\3{\4}\def\4{\5}
\long\gdef\a{\expandallonce{\0{{} {\2} {\3}\0\2}\2}}
\expandonce\a\show\a
\expandonce\a\show\a
\expandonce\a\show\a
For \reverseexpand, we first insert code from this question on building a multi-\expandafter before every token. The next step of expansion will trigger the multi-expansion, which does what you want. Note that the macros we expand can happily take all sorts of arguments, because we did not leave anything in the input stream after the token we are currently expanding.
% Code based on the extended Upper- and Lower-casing code found
% in the ULcase package.
\input ULcase.sty\relax
% ============ Table |reverseexpand|
% With the definition below, one step of expansion on
% |\romannumeral\eatwice| is the same as two steps of |\expandafter|.
\gdef\eatwice{0\expandafter\expandafter\expandafter\space%
\expandafter\expandafter\expandafter}
% We insert |\romannumeral\eatwice| in front of every token,
% and add |\empty\empty| at the end, to stop the extra |\expandafter|.
%
% The full expansion requires 5 steps.
\long\gdef\reverseexpand#1{%
\UL_to_case:nn{reverseexpand}{#1}\empty\empty}
% The default action is to output the given token plus the extra text.
\long\gdef\UL_table_reverseexpand_default#1{%
\UL_to_case_output:n{\romannumeral\eatwice#1}}
% The chain of |\expandafter| triggers the full expansion of
% |\romannumeral\UL_to_case_aux:nn{#1}{#2}|, which adds the
% tokens |\romannumeral\eatwice| before each token in the group.
%
\long\gdef\UL_table_reverseexpand_braces#1#2{%
\expandafter\UL_to_case_output:n\expandafter{%
\expandafter\romannumeral\expandafter\eatwice\expandafter{%
\romannumeral\UL_to_case_aux:nn{#1}{#2}%
\romannumeral\eatwice%
}%
}%
}
% ===== Tests
\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{\0}
\def\foo#1{{Foo=#1.}}
\def\double#1{(#1,#1)}
\long\def\a{\reverseexpand{\double\foo{ \4\6}\8}}
\expandonce\a\show\a
\expandonce\a\show\a
\expandonce\a\show\a
\expandonce\a\show\a
\expandonce\a\show\a
The relevant part of ULcase (should be put at the top of the other pieces of code, but that's not supposed to be the key point).
\catcode`\_=11\relax
\catcode`\:=11\relax
% ======================== Generic macros
% Note on LaTeX3's naming convention: letters after ":" in macro names
% indicate the arguments that the command takes.
% "n" = braced
% "N" = single token
% "w" = weird
% and plenty of others.
%
% A few standard commands to manipulate arguments
\long\gdef\use_i:nn#1#2{#1}
\long\gdef\use_ii:nn#1#2{#2}
% What expl3 calls "quarks", useful for |\ifx| comparisons.
\gdef\q_stop{\q_stop}
\gdef\q_mark{\q_mark}
\long\gdef\use_none_until_q_stop:w#1\q_stop{}
% Two tests
\long\gdef\UL_if_empty:nTF#1{%
\expandafter\ifx\expandafter\q_mark\detokenize{#1}\q_mark%
\expandafter\use_i:nn%
\else%
\expandafter\use_ii:nn%
\fi}
\expandafter\long\expandafter\gdef\expandafter\UL_if_detok_qmark:wTF%
\expandafter#\expandafter1\detokenize{\q_mark}#2\q_stop{%
\UL_if_empty:nTF{#1}}
% ======================== Main command: |\UL_to_case:nn|
% Usage: |\UL_to_case:nn{<table>}{<text>}|
% Expands in: 2 steps.
\long\gdef\UL_to_case:nn{\romannumeral\UL_to_case_aux:nn}
\long\gdef\UL_to_case_aux:nn#1#2{-`\0% almost stops \romannumeral
\UL_brace_check:nw{#1}#2{\q_mark} \q_stop\UL_to_case_end:n{}}%
% |\UL_to_case_output:n| appends its argument to the argument of
% |\UL_to_case_end:n|.
\long\gdef\UL_to_case_output:n#1#2\UL_to_case_end:n#3{%
#2\UL_to_case_end:n{#3#1}}
\long\gdef\UL_to_case_end:n#1{ #1}
% And |\UL_to_case_end:n| expands to
% - a space, which stops the expansion of |\romannumeral-`\0|,
% - followed by its argument, which is the result we want.
% First, we check whether the next token is a brace.
\long\gdef\UL_brace_check:nw#1#2#{%
\UL_if_empty:nTF{#2}%
{\UL_brace_yes:nn{#1}}%
{\UL_space_check:nw{#1}#2}%
}
% If there is a brace, we might have reached {\q_mark}.
\long\gdef\UL_brace_yes:nn#1#2{%
\expandafter\UL_if_detok_qmark:wTF \detokenize{#2 \q_mark}\q_stop{%
% Note the space before \q_mark!
\use_none_until_q_stop:w%
}{% Otherwise, we have a brace group, and we can act on it.
\csname UL_table_#1_braces\endcsname{#1}{#2}%
\UL_brace_check:nw{#1}%
}%
}
% Then check whether the next token is a space.
\long\gdef\UL_space_check:nw#1#2 {%
\UL_if_empty:nTF{#2}%
{\UL_convert_token:nn{#1}{ }}%
{\UL_convert_token:nn{#1}#2 }% put the space back!
}
\long\gdef\UL_convert_token:nn#1#2{%
\ifcsname UL_table_#1_\detokenize{#2}\endcsname%
\expandafter\use_i:nn%
\else%
\expandafter\use_ii:nn%
\fi%
{\csname UL_table_#1_\detokenize{#2}\endcsname}%
{\csname UL_table_#1_default\endcsname{#2}}%
\UL_brace_check:nw{#1}% Do the next token.
}
% For tests.
\long\gdef\expandonce#1{% redefines #1 as #1 expanded once.
\long\xdef#1{\unexpanded\expandafter\expandafter\expandafter{#1}}}
\reverseexpansion? How does it know when to stop reading arguments? And what do you mean ‘reverse the order of expansion’? A more concrete example would help. – Will Robertson Feb 16 '11 at 07:23\reverseexpansion2\a\b\cto reverse the expansion of\aand\b, leaving\cuntouched in that run. I hope this helps.\reverseexpansion2\a\b\cis equivalent to\expandafter\a\b\cand\reverseexpansion3\a\b\cis\ea\ea\ea\a\ea\b\c? – Will Robertson Feb 16 '11 at 07:49\expandafter's. I know there is a formula relating the number of\expandafter'sto the order of reversing, but listing out all the needed\expandafter'sis inefficient. – Ahmed Musa Feb 16 '11 at 07:57\reverseexpansion4\a{\b\c}\dsupposed to result in? – Will Robertson Feb 16 '11 at 07:573or4?\reverseexpansion3\a{\b\c}\dshould yield the expansion of\d,\c,\band\ain that order, but the braces around\band\cshould not disappear thereafter. After (or actually during the chain of expansions)\awill act on expanded{\b\c}, and the result of that will act on expanded\d. This is an over-simplification but I hope it helps. Braces are the real source of the complication here. – Ahmed Musa Feb 16 '11 at 09:42\@selective@sanitizemacro in thexkeyvalpackage, which 'walks' through tokens, selectively sanitizing tokens and respecting groups signified by balanced braces. A beautiful macro. – Ahmed Musa Feb 16 '11 at 09:43tedpackage. Now that's nice. But note this and\@selective@sanitizearen't expandable! Respecting braces and being expandable is either difficult or impossible. (I thought impossible except Bruno Le Floch mentioned he could do it recently. I still haven't looked at the code, through.) Regarding your\reverseexpansion, I don't really see the point. It seems more abstract than useful, to me. – Will Robertson Feb 16 '11 at 10:59\reverseexpansion3{\a{\b\c}\d}or\reverseexpansion3\a{\b\c}\d+(with any given delimiter in place of+). As long as it is possible to place a sentinel{}at the end of the argument, it is possible to distinguish braced vs unbraced arguments expandably. – Bruno Le Floch Feb 16 '11 at 16:19\expandsomeincluded in the pre-packageULcase.sty. It allows to selectively expand some tokens, and preserves braces and spaces. – Bruno Le Floch Feb 16 '11 at 16:40\sea):\sea3\a\sea3\b\sea3\c...\sea3\l\sea1\m\n.