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.

In Expandable full expansion of tokens that preserves catcodes, Joseph Wright presents the following code:

\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{}

Of which he says:

I'd also note that the above code needs some guards adding for a blank (empty or all space) argument, as currently things fail in these cases.

And this is true enough, if not exactly grammatical. Unfortunately, I'm not up to scratch on my TeX, so I'm not sure how to add such guards. Any takers?

(For my application, anything that will work in pdfTeX is fine.)

share|improve this question
add comment

1 Answer

up vote 7 down vote accepted

This version needs exactly two expansions to work, and avoids needing the \csname construction by using two \romannumeral applications instead. The outer one makes sure we need exactly two expansions, the inner one does the expanding:

\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}

The previous version coded in a test for an entirely blank argument, but this fails if the argument is not blank but expands to something which is:

\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{ }
share|improve this answer
    
isn't this missing a definition of something like \def\fullyexpandblank#1#2{ } otherwise I get an undefined command error if there are any spaces in the original arg, I was trying to see if spaces can be preserved (rather than stripped) but that seems a little tricky to do via expansion only as the primitive argument grabber has a tendency to eat space –  David Carlisle Mar 9 '12 at 20:32
    
@DavidCarlisle Yes, I'd forgotten to copy-paste that line. –  Joseph Wright Mar 9 '12 at 20:48
    
Huh. Now I discover that this doesn't seem to work for stuff like, say, \fullyexpand{\gobble{1}}, due to the way it loops over the input one token at a time, so you end up with \gobble and 1 instead of nothing :-(. –  SamB Mar 11 '12 at 1:42
    
... but, since it is what I literally asked for, and it turned out that all I really needed was an \edef, I'm accepting it ;-). –  SamB Mar 11 '12 at 17:25
add comment

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.