1

I have a macro \a. I'd like to define a macro \b, whose replacement text, T, will satisfy the following requirements:

  1. The catcode of every token of T is in the range 10-12.
  2. Ignoring catcodes, T is token-by-token equal to \a's ultimate expansion.

For instance,

\def\z{12pt}%
\def\a{\noindent\fontsize{10pt}{\z}\selectfont}%
\def\b{???}%
\b

should typeset:

\noindent\fontsize{10pt}{12pt}\selectfont

If this is not possible, it will be OK, too, if \b expands everything recursively, until there's nothing more to expand.

Finally, the following example, too, will be good enough:

\def\a{\noindent\fontsize{10pt}{\a}\selectfont}%
\def\b{???}%
\b

should typeset:

\noindent\fontsize{10pt}{\a}\selectfont

  • Please make a MWE. – TeXnician Aug 5 '17 at 9:09
  • "Ultimate expansion" -- at the time of definition (edef)? Or use (which is impossible)? – ShreevatsaR Aug 5 '17 at 9:15
  • @ShreevatsaR: If possible, at the time of use. – Evan Aad Aug 5 '17 at 9:16
  • @TeXnician: I've added a MWE. – Evan Aad Aug 5 '17 at 9:17
  • 1
    How does macro \b know that it should expand \z but not \fontsize and \selectfont? – Heiko Oberdiek Aug 5 '17 at 9:21
8

Without expansion of \a and with \meaning:

\documentclass{article}
\begin{document}
\def\z{12pt}%
\def\a{\noindent\fontsize{10pt}{\z}\selectfont}%
\makeatletter
\edef\b{\expandafter\strip@prefix\meaning\a}
\makeatother
\texttt{\b}
\end{document}

Result

The spaces after command names are inserted by TeX.

Variant with e-TeX's \detokenize:

\edef\b{\detokenize\expandafter{\a}}

In both cases, the tokens in the definition text of \b have category code 12 (other) and the space has category code 10 (space).


If the contents of \a is more or less known, then the macros that should not be expanded can be prevented from expansion by redefining them to the meaning of \relax:

\documentclass{article}
\begin{document}
\def\z{12pt}%
\def\a{\noindent\fontsize{10pt}{\z}\selectfont}%

\begingroup
  \let\fontsize\relax
  \let\selectfont\relax
  \edef\x{\a}
  \xdef\b{\detokenize\expandafter{\x}}
\endgroup
\texttt{\b}
\end{document}

Result

A full expansion of \a only works for some macros with more or less useful result:

\documentclass{article}
\begin{document}
\def\z{12pt}%
\def\a{\noindent\fontsize{10pt}{\z}\selectfont}%

\edef\tmp{\a}
\edef\b{\detokenize\expandafter{\tmp}}
\endgroup
\texttt{\b}
\end{document}

Result

Adding package microtype, for example, will get a macro \a that breaks in a hard expansion by \edef:

! Argument of \MT@res@a has an extra }.
<inserted text>
                \par
l.7 \edef\tmp{\a}

LaTeX's protection mechanism can be used by the softer \protected@edef. Then, commands defined by \DeclareRobustCommands and commands with optional arguments are not expanded too much:

\documentclass{article}
\usepackage{microtype}
\begin{document}
\def\z{12pt}%
\def\a{\noindent\fontsize{10pt}{\z}\selectfont}%

\makeatletter
\protected@edef\tmp{\a}
\makeatother
\edef\b{\detokenize\expandafter{\tmp}}
\texttt{\b}
\end{document}

Result

  • Thank you. Is there also a version in which \a is expanded recursively until there's nothing left to expand? – Evan Aad Aug 5 '17 at 9:28
  • 1
    @EvanAad First \edef\a{\a} and then \edef\b{...} from above. – Henri Menke Aug 5 '17 at 9:29
  • The \strip@prefix is not very reliable, see the discussion in Is there a reliable way to get the number of arguments of a command?. – Henri Menke Aug 5 '17 at 9:31
  • By the way, will this also work when \a's expansion is ill formed? I don't even know how to write it, but something along the lines of \a\def{\macro{}, where the replacement string has just an opening brace. – Evan Aad Aug 5 '17 at 9:31
  • 1
    @HenriMenke \def\a{\def\foobar{...}} with \foobar previously undefined will break in an \edef, because \def is not executed and \foobar is undefined. – Heiko Oberdiek Aug 5 '17 at 9:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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