\expanded
primitive that would fully expand its argument, similar to the full expansion that happens for the replacement text of an \edef
. However, there is a trick that will keep expanding macros until it reaches an unexpandable token.This trick depends on an obscure aspect of TeX's grammar. Namely, wherever TeX expects a number, there are 4 different ways to give it a literal (and several more ways besides): (1) as a decimal number
1234
, (2) as an octal number '31
, (3) as a hexadecimal number "AA55
, or (4) as the character code of a character token `X
. For any of those, the literal can be preceded by an optional string of minuses (each one negates the number that is to follow so --5
is the same as 5
) and can be followed by an optional space token. For example,\count@=-37
contains both the optional minus and the optional space (because the newline was turned into a space token).We can use case (4) along with the fact that
\romannumeral
expands to nothing when the following number is nonpositive to produce a full expansion:\romannumeral-`X\foo
\foo
will expand until there is a single nonexpandable token. Why does this work? Well, -`X
is a complete number, namely -88. Since it is negative, \romannumeral-`X
expands to nothing, as we would expect. But that leaves \foo
. Remember that optional space in the grammar? It turns out that TeX will now expand tokens until it finds something unexpandable in its search for that optional space. The downside to this is that if \foo
expands to something that starts with a space, the space will disappear.As an example of this hack, consider the
\hexnumber
macro. This macro does not produce any nonexpandable tokens until it is ready to produce all of them. This makes it a fine candidate for the \romannumeral
hack.\expandafter\def\expandafter\foo\expandafter{\romannumeral-`X\hexnumber{37}}
\show\foo
\expandafter\def\expandafter\bar\expandafter{\hexnumber{37}}
\show\bar
If we examine TeX's output, the difference is immediately obvious.> \foo=macro:
->25.
> \bar=macro:
->\ifnumcomp {37}<0{}{\hn@i {37}{}}.
Thanks to Joseph Wright for pointing this out.
I have used this \romannumeral trick for other purposes too.
ReplyDelete