# “Activate” active characters in argument passed as macro

Let's say I have some macro (\iPrint in the MWE) that internally uses active characters to interpret its argument:

\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}

\begingroup
\lccode\~=\|
\lowercase{\endgroup
\def~#1~{\textbf{#1}}%
}
\def\iPrint{%
\begingroup
\catcode\|=\active
\iPrintX
}
\def\iPrintX#1{%
#1
\endgroup
}

\begin{document}
\def\temp{This |doesn't work|, though :-(}
\begin{itemize}
\item \iPrint{This |works pretty| well!}
\item \iPrint{\temp}
\end{itemize}
\end{document}


This yields:

AFAIU the issue, catcodes are assigned at definition time of a macro: When \temp is defined, | is not active. However, is there something I can do within \iPrint to make it active anyway? Some sort of recoding the argument with the current catcodes?

When you say

\def\temp{This |doesn't work|, though :-(}


the catcode of | is fixed and setting \catcode|=\active has no effect when the expansion of \temp is performed: the | characters in the replacement text have category code 12.

One can use \scantokens to reassign category codes, but the macros must be expanded before \scantokens comes into play:

\makeatletter
\begingroup
\lccode\~=\|
\lowercase{\endgroup
\def~#1~{\textbf{#1}}%
}
\def\iPrint#1{%
\begingroup
\catcode\|=\active
\protected@edef\reserved@iprint{\noexpand\scantokens{#1\noexpand\empty}}\reserved@iprint
\endgroup
}
\makeatother


If the input to \iPrint is known to be only text or a macro (say \iPrint{\temp}), then the simpler

\def\iPrint#1{%
\begingroup
\catcode\|=\active
\scantokens\expandafter{#1\empty}%
\endgroup}


would work. If the argument is more complicated (text and macros), then the more complicated approach with \protected@edef appears to be necessary; it will expand all macros, but won't go too much deeply with "protected" macros.

The \empty at the end of the argument to \scantokens is customary to avoid a spurious space: \scantokens actually emulates the input of a file and all files are implicitly terminated with an empty line. Inside \protected@edef it mustn't be expanded, so we add \noexpand in front of it.

(Thanks to Bruno Le Floch for correcting an inaccuracy.)

• Your last paragraph is inaccurate. The \noexpand at the end of the argument to \scantokens can be replaced by \relax, or by \empty. It's only effect is that the last line of the virtual file built by \scantokens ends with a control word, hence a newline doesn't produce a space. As it is, your \noexpand acts on \endgroup, which luckily does nothing bad. – Bruno Le Floch Jan 29 '12 at 0:10

Catcodes are assigned when a input character is read. TeX then looks up the current value for this input and attaches the catcode. Later it can not be changed. So you must reread the input if you want to change it. E.g. by writing it to a file and then reinput this file. Or by using \scantokens.

\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}

\begingroup
\lccode\~=\|
\lowercase{\endgroup
\def~#1~{\textbf{#1}}%
}
\def\iPrint{%
\begingroup
\catcode\|=\active
\iPrintX
}
\def\iPrintX#1{%
#1
\endgroup
}

\begin{document}
\def\temp{This |doesn't work|, though :-(\relax}
\begin{itemize}
\item \iPrint{This |works pretty| well!}
\item \iPrint{\scantokens\expandafter{\temp}}.
\item \iPrint{\temp}.
\end{itemize}
\end{document}
`
• Thanks! However, I was looking for a way to do it on the callee side, which is what egreg's solution provides. – Daniel Jan 28 '12 at 17:34