The \scantokens
command is not “dangerous” by itself, but it can have surprising effects.
How does it work? Its argument is scanned like for a \write
operation, but all symbolic tokens are considered unexpandable, based on the current category codes; the result is placed in a “pseudofile” that is read in exactly as if \input
was used.
This has various consequences; for instance an implicit space token is added at the end, actually an end-of-line character, exactly like TeX does with \input
. This space can be neutralized in various ways, but the most common one is adding \empty
or \noexpand
at the end of the token list for \scantokens
. Using \ignorespaces
is not in my list of recommendations:
\scantokens{a\ignorespaces}X\scantokens{a\empty}X
\scantokens{a\ignorespaces} X\scantokens{a\empty} X
will output
so you see the effect of \ignorespaces
goes beyond \scantokens
.
One might be tempted to use \scantokens
for getting verbatim in the argument to a command, but there's a catch:
\def\test#1{\begingroup\tt\catcode`\\=12
\scantokens{#1}\endgroup}
\test{\abc\def}
will output
(I'm using Plain TeX for simplicity). Where does the space come from? In the internal representation for \write
, control words are followed by a space; the change in category code for the backslash happens too late.
Another (admittedtly tricky) quirk:
\def\test#1{\begingroup\tt\catcode`\\=12
\scantokens{#1}\endgroup}
\newlinechar`^^J
\test{a^^Jb^^J^^Jc}
will make TeX read the pseudofile as if it were
a
b
c
but the result is not
a b\par c
as it could be expected; rather, it is
because TeX converts the two consecutive end-of-lines into the token \par
, not into the control sequence \par
. This is proved by
\def\test#1{\begingroup\tt\catcode`\\=12 \edef\par{\string\par}%
\scantokens{#1}\endgroup}
\newlinechar`^^J
\test{a^^Jb^^J^^Jc}
that outputs
Exercise: explain the details.
Final quirk: one can use \scantokens\expandafter{\foo}
and \foo
will be expanded before \scantokens
does its job:
\def\test#1{\begingroup\tt\catcode`\\=12
\scantokens\expandafter{#1}\endgroup}
\def\foo{\abc\def}
\test\foo
\bye
will do the same as before
In order to have \scantokens
inside \edef
, one needs one more trick: the end-of-file must be hidden.
\def\escantokens#1#2#3{%
\begingroup\everyeof{\noexpand}%
#3%
\edef\x{\endgroup\def\noexpand#1{\scantokens{#2}}}\x
}
\escantokens\demo{\abc\def}{\catcode`\\=12 }\show\demo
\escantokens\demo{\abc}{\def\abc{ABC}}\show\demo
\bye
The third argument to \escantokens
is a set of temporary assignments (category codes, for instance, but not only).
The output on the terminal is
> \demo=macro:
->\abc \def .
l.8 \show\demo
?
> \demo=macro:
->ABC.
l.10 ...tokens\demo{\abc}{\def\abc{ABC}}\show\demo
?