[Tex/LaTex] A \typeout (or expandable command) that can expand once – or with variable levels of expansion

debuggingexpansionmacrosterminal-outputtex-core

Consider the following MWE:

\documentclass[12pt]{article}

\begin{document}

\def\aaa{something}
\typeout{=1==\aaa==}
\typeout{=1==\meaning\aaa==}
\typeout{}

\def\bbb{else \aaa, else}
\typeout{=2==\bbb==}
\typeout{=2==\meaning\bbb==}
\typeout{}

\def\ccc{third \bbb, level}
\typeout{=3==\ccc==}
\typeout{=3==\meaning\ccc==}
\typeout{}

\end{document}

It outputs the following to terminal:

=1==something==
=1==macro:->something==

=2==else something, else==
=2==macro:->else \aaa , else==

=3==third else something, else, level==
=3==macro:->third \bbb , level==

Thus, I'd consider (in this case) \meaning to provide unexpanded contents of a macro (that would be "null" level of expansion); while \typeout does "infinite" expansion (until no more tokens to be expanded). I've noted that \typeout calls \write:

$ texdef -t latex -c book -p titletoc typeout -f
\typeout is defined by (La)TeX.

\typeout:
macro:#1->\begingroup \set@display@protect \immediate \write \@unused {#1}\endgroup 

… so this "infinite" expansion behavior may be due to \write.

 

What I'd like, eventually, is an expandable function, which will accepts a numeric "level of expansion", and could be called like this:

# just a fake interface definition for testing
\def\expandlevel#1\nil#2\nil{
---#1---#2--
}

\edef\yyy{\expandlevel0\nil\ccc\nil}
\typeout{\yyy}

… such that:

  • When I call for level null expansion – what I get when I typeout are unexpanded contents (like \meaning); pseudo:
    * \typeout{==\expandlevel0\nil\ccc\nil==}
    ==third \bbb , level==
  • When I call for a single expansion – what I get is (pseudo):
    * \typeout{==\expandlevel1\nil\ccc\nil==}
    ==third else \aaa , else , level==
  • When I call for infinite expansion – that would be 99 levels :) – what I get is the same as \typeout would perform; pseudo:
    * \typeout{==\expandlevel99\nil\ccc\nil==}
    ==third else something, else, level==

Unexpandable/protected tokens would remain (or be shown with \inaccessible), as \typeout performs currently..

In lack of a "real" expandable macro like that, are there any tricks to coax \typeout to behave in similar fashion (if possible)?

Best Answer

This has nothing to do with \typeout except insofar as its argument is fully expanded because of \write, as you noted. You could ask the same of \edef, and the answer would be to use a sufficiently elaborate combination of \expandafter, \noexpand, and \unexpanded to force each token to expand the desired number of times. For example:

*\def\a{\b} \def\b{\c} \def\c{c}

*\edef\x{\noexpand \a}

*\show\x 
> \x=macro:
->\a .
<*> \show\x

? 

*\edef\x{\expandafter\noexpand \a}

*\show\x
> \x=macro:
->\b .
<*> \show\x

? 

*\edef\x{\expandafter\expandafter\expandafter\noexpand \a}

*\show\x
> \x=macro:
->\c .
<*> \show\x

? 

*\edef\x{%
  \expandafter\expandafter\expandafter\expandafter
  \expandafter\expandafter\expandafter\noexpand\x}

*\show\x
> \x=macro:
->c.
<*> \show\x

The number of \expandafters increases exponentially. The question of how to write a single expandable command that expands the desired number of times is addressed here. Note that the answer given there still only expands one token, so you can't apply it to an entire text. It is possible to play tricks with \unexpanded (a \noexpand for many tokens) and with token list registers (which, inside \edef, expand to their contents and then make the result temporarily unexpandable) to achieve similar effects.

You might be wondering, if zero expansions and infinite expansions are allowed on an entire text, why not one or more? The answer is that in fact, "infinite" expansions are done token-by-token and there is no notion of expanding the "whole text". To do zero expansions TeX simply has to dump the token list as given; the way that \meaning apparently does one expansion is that it actually changes the \catcodes of its output so that it is no longer expandable: those aren't real macros you see. You can achieve the same effect yourself if you use \scantokens in combination with a \catcode change...except that \catcode is not expandable, so you can't do that in \edef or \write. Or rather, if you do, you will just see the string "\catcode" in the result.

Anyway, if you want to expand a "whole text" a certain number of times, you could mean one of three things:

  1. Expand the first token, and then expand the first token of the result, and so on, that many times.

  2. Expand the first expandable token, then the first expandable token of the result, and so on, that many times.

  3. Expand the first expandable token; then, in the expanded text, expand everything (n - 1) times in the same way. Once you're done, move to the first "untouched" token and continue in this way to the end of the text. (Thus, expanding to a certain "depth" across the entire text.)

Rules 1 and 2 have different results:

\def\a{a}
a\a

where expanding "once" gives either "a\a" or "aa" depending on the rule. Rule 1 is relatively easy to implement using \multiexpandafter (from the other question) and \unexpanded. Rule 2 is probably impossible without embedding \expandafter and \noexpand into all your macros. Rule 3 is probably Turing-complete.

Related Question