Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

Is there a non-expl3 expandable function for reversing the order of expansion of an arbitrary number of tokens or macros? For example,

  \reverseexpansion<n>\a\b
  \reverseexpansion<n>\a\b\c
  \reverseexpansion<n>\a\b\c\d
  \reverseexpansion<n>\a\b\c\d\e ... \m

where <n> is the number of leading tokens whose expansions are to be reversed.

Also, an expandable version of \expandallonce, which expands all the macros in its argument precisely only once, will be useful:

  \expandallonce{\a\b}
  \expandallonce{\a\b\c}
  \expandallonce{\a\b\c\d}
  \expandallonce{\a\b\c\d\e ... \n}

I should add that the tokens may contain balanced braces/arguments. Say,

\reverseexpansion 3\a{\b\c}\d
\expandallonce{\a{\b\c}\d}

The braces aren't expandable, but should be preserved after the run.

Actually, I already have awkward unexpandable solutions for the two tasks. Rather than spend time finding expandable solutions, I thought someone would have worked them out in the past.

share|improve this question
    
Can you be more clear about \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 7:23
1  
You have a point. I left out the <number> of arguments to reverse. The number of arguments will be specified as the first argument. So we may call \reverseexpansion2\a\b\c to reverse the expansion of \a and \b, leaving \c untouched in that run. I hope this helps. –  Ahmed Musa Feb 16 '11 at 7:37
    
@Ahmed So \reverseexpansion2\a\b\c is equivalent to \expandafter\a\b\c and \reverseexpansion3\a\b\c is \ea\ea\ea\a\ea\b\c ? –  Will Robertson Feb 16 '11 at 7:49
    
@Will: Yes, but the count can get messy after seven \expandafter's. I know there is a formula relating the number of \expandafter's to the order of reversing, but listing out all the needed \expandafter's is inefficient. –  Ahmed Musa Feb 16 '11 at 7:57
    
@Ahmed And what is \reverseexpansion4\a{\b\c}\d supposed to result in? –  Will Robertson Feb 16 '11 at 7:57

2 Answers 2

up vote 2 down vote accepted

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}}}
share|improve this answer
    
I'm slightly wary of a solution using package code which is not on CTAN. (More generally, I wonder if tex-core questions should always have fully stand-alone answers: one for meta.) –  Joseph Wright Feb 16 '11 at 17:54
    
See my meta post: meta.tex.stackexchange.com/questions/958 (I have no problem with either answer, by the way) –  Joseph Wright Feb 16 '11 at 18:01
    
@Joseph: yes, I hesitated, but I really don't feel like rewriting the same code. I can extract the 60 or so lines that are required, and put them here if it is better. –  Bruno Le Floch Feb 16 '11 at 18:57
    
@Bruno: Many thanks for your effort. I will study your solution. It contains something I can build on. –  Ahmed Musa Feb 16 '11 at 19:02
    
@Bruno: Please can you provide us with a web link for your solution? It will be easier to copy from there. –  Ahmed Musa Feb 16 '11 at 19:11

\reverseexpansion

I'm going to pass on this since I think what you're asking would be approximately as difficult or perhaps harder than the answers given to this question, and it's not clear to me if any of these expansion abstractions are actually useful in practise. (And I'm out of time.)

\expandallonce

The idea is to map over the list token by token and apply \unexpanded\expandafter{#1} to each. But you get an expl3 solution anyway, because I'm tired of re-programming the same code over and over :)

\usepackage{expl3}
\ExplSyntaxOn
\def\expandallonce#1{
  \tl_map_function:nN {#1} \tl_nested_exp_not:n
}
\cs_set:Nn \tl_nested_exp_not:n {
  \tl_if_single:nTF {#1}
    { \exp_not:o #1 }
    { { \expandallonce {#1} } }
}
\ExplSyntaxOff
\def\a{\aa}\def\b{\bb}\def\c{\cc}\def\d{\dd}
\edef\1{\expandallonce{\a{\b\c}\d}}
\show\1

This is not completely robust if you want to put arbitrary TeX code inside \expandallonce, but I assume that this is not a problem. (E.g., you can't put conditionals inside.) Note that expl3 currently doesn't have a mapping function that nests through braces so (in the updated answer) I've achieved a similar effect with recursion.

Note this nested mapping does respect braces unless the braces surround a single token, such as in abc{d}efg. I'm not aware of any way around this problem and have the code remain expandable. (Perhaps with \detokenize and \scantokens it's possible, but that's too fragile for my liking.)

share|improve this answer
    
Joseph Wright has ever suggested this 'non-robust' expl3 solution to me, but I am not yet a convert to LaTeX3. I am not sure why I am cold to LaTeX3? As Joseph ever said here, I do think something like etoolbox or etextools would have been preferred to LaTeX3 by some of us. Please no one should resume another discussion on LaTeX3 here. :) The question isn't about LaTeX3. –  Ahmed Musa Feb 16 '11 at 7:53
3  
@Ahmed — I'm posting expl3 code because it's quick for me to write and demonstrates the algorithm. It's not too hard to translate if you want to use a different programming layer. –  Will Robertson Feb 16 '11 at 7:56

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.