# Active character and delimited argument

Say I have a macro \foo that takes one argument, delimited at start and at end by a !. The problem is that I don't know at the time of the call of \foo if ! has been made \active by, say, french babel.

For now, I use the following approach. It works but I need to maintain two version of the same macros, one dealing with non active ! and the other with active !.

\documentclass{standalone}

\makeatletter
\begingroup
\catcode\!=12\relax
\gdef\foo@nonactive!#1!{Non active: #1.}
\catcode\!=\active\relax
\gdef\foo@active!#1!{Active: #1.}
\endgroup

\def\foo{%
\ifnum\the\catcode\!=\active\relax
\expandafter\foo@active
\else
\expandafter\foo@nonactive
\fi}
\makeatother

\begin{document}

\catcode\!=12\relax
\foo!bar!
\catcode\!=\active\relax
\def !{This exclamation mark is active}
\foo!bar!!

\end{document}


Question

Is this approach robust and is there an other way to do that (with and/or without eTeX).

-
If this turns out to be a good approach, I can reformulate the question and post the code as an answer. –  cjorssen May 1 '12 at 17:36
I'd avoid an expansion step by saying \AtBeginDocument{\ifnum\catcode\!=\active\let\foo\foo@active\else\let\foo\foo@‌​nonactive\fi} as babel activates the characters at begin document. –  egreg May 1 '12 at 17:51
@egreg Ok. But I forgot to say that it must be generic. –  cjorssen May 1 '12 at 18:00
@egreg The \AtBeginDocument trick must be written after \usepackage[french]{babel} if I understand correctly, as \AtBeginDocument builds a kind of FIFO stack. –  cjorssen May 1 '12 at 21:45
Yes, of course. One can use \AfterEndPreamble of etoolbox to ensure that the code is executed after babel activations. –  egreg May 1 '12 at 21:58

An alternative is:

\documentclass{standalone}

\makeatletter

\def\foo#1{\def\foo@##1#1{[non]active ##1}\foo@}

\makeatother

\begin{document}

\catcode\!=12\relax
\foo!bar!
\catcode\!=\active\relax
\def !{This exclamation mark is active}
\foo!bar!!

\end{document}


As noted by egreg in the comments this definition is "fragile", if you try

\tableofcontents
\section{ aaa \protect\foo\protect!ddd\protect! bbb }


Things die horribly as the inner \def inside \foo can not happen during a \write (to write the .toc file).

If you try

\tableofcontents
\section{ aaa \protect\foo!ddd! bbb }


Then @egreg pointed out that things would still fail as then \foo would not expand, but both ! would expand so would not be available as delimiters. Shorthands can in fact be defined to be self-protecting so in an environment that requires protection they should expand to themselves (like \noexpand!).

\documentclass{article}

\makeatletter

\def\foo#1{\def\foo@##1#1{[non]active ##1}\foo@}

\makeatother

\begin{document}

\catcode\!=12\relax
\foo!bar!
\catcode\!=\active\relax
\def !{\ifx\protext\relax This exclamation mark is active\else\noexpand!\fi}
\foo!bar!!

\tableofcontents

\section{ aaa \protect\foo!ddd! bbb }

\end{document}

-
However this is not expandable and with \protect in front of it, a following active character would be expanded. :( –  egreg May 1 '12 at 19:18
@egreg don't move the goal posts:-) anyway presumably in a setting needing protect the active character would/should be defined to expand to itself so everything will work out OK in the end. –  David Carlisle May 1 '12 at 19:22
@DavidCarlisle Hum interesting since expandability is not a requirement. Could you please add some details regarding egreg's comment and yours. Thanks. –  cjorssen May 1 '12 at 20:16
answer expanded as requested –  David Carlisle May 1 '12 at 20:40
@DavidCarlisle Ok. Thanks. And what do you mean by "would/should be defined to expand to itself so everything will work out OK in the end"? –  cjorssen May 1 '12 at 20:51
show 1 more comment

Here is a general solution (taken from regexpatch.sty):

\documentclass{article}
\usepackage[french]{babel}
\usepackage{expl3}

\ExplSyntaxOn
\seq_new:N \g_ddef_commands_seq
\tl_new:N \l_ddef_prefix_tl
\tl_new:N \l_ddef_arg_tl
\tl_new:N \l_ddef_replacement_tl
\cs_new_protected:Npn \delimited #1 \def #2
{
\seq_gput_right:Nn \g_ddef_commands_seq { #2 }
#1 \tex_def:D #2
}
\AtBeginDocument{
\seq_map_inline:Nn \g_ddef_commands_seq { \ddef_normalize:N #1 }
}
\cs_new_protected:Npn \ddef_normalize:N #1
{
\tl_set:Nf \l_ddef_prefix_tl { \token_get_prefix_spec:N #1 }
\tl_set_rescan:Nnx \l_ddef_prefix_tl { } \l_ddef_prefix_tl
\tl_set:Nf \l_ddef_arg_tl { \token_get_arg_spec:N #1 }
\tl_set_rescan:Nnx \l_ddef_arg_tl { } \l_ddef_arg_tl
\tl_set:Nf \l_ddef_replacement_tl { \token_get_replacement_spec:N #1 }
\tl_set_rescan:Nnx \l_ddef_replacement_tl { } \l_ddef_replacement_tl
\use:x
{
\exp_not:V \l_ddef_prefix_tl
\tex_def:D
\exp_not:N #1
\exp_not:V \l_ddef_arg_tl
{ \exp_not:V \l_ddef_replacement_tl }
}
}
\ExplSyntaxOff

\delimited\def\foo!#1!{Whatever with #1}

\delimited\long\def\bar+#1+{Another with #1}

\begin{document}

\foo!x!

\number\catcode!

\bar+y+

\number\catcode+

\show\bar
\end{document}


This will print

Whatever with x
13
Another with y
12

Basically we remember all commands that are defined with \delimited\def; prefixes such as \long or \protected must go between those two tokens, but using \protected can open a can of worms if the command is used in a moving argument. Then at begin document, so after babel has possibly activated some characters, the commands are rebuilt under the current category codes.

Notice that also the tokens in the replacement text will possibly change category code, but this shouldn't be a problem.

By loading regexpatch the code is simpler:

\usepackage{regexpatch}
\ExplSyntaxOn
\seq_new:N \g_ddef_commands_seq
\cs_new_protected:Npn \delimited #1 \def #2
{
\seq_gput_right:Nn \g_ddef_commands_seq { #2 }
#1 \tex_def:D #2
}
\AtBeginDocument{
\seq_map_inline:Nn \g_ddef_commands_seq { \ddef_normalize:N #1 }
}
\cs_new_protected:Npn \ddef_normalize:N #1
{
\xpatch_get_all:N #1
\xpatch_rebuild:N #1
}
\ExplSyntaxOff


## Note

If we want to be sure that the code is performed after babel has activated the characters (but babel should be among the package loaded very early) one can say

\usepackage{etoolbox}


and use \AfterEndPreamble instead of \AtBeginDocument.

-
Thanks for your efforts. This approach is really interesting but unusable in my case since I need something generic. –  cjorssen May 2 '12 at 16:19
@cjorssen This is generic! Please, make an example. –  egreg May 2 '12 at 17:35
Really sorry. Maybe wrong vocabulary. I meant I cannot rely on expl3. I'm looking for something working out of the box with a pure TeX-syntax for plain, latex or context formats. However, I must acknowledge it was not stated in my question. –  cjorssen May 2 '12 at 19:57