6

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).

5
  • If this turns out to be a good approach, I can reformulate the question and post the code as an answer.
    – cjorssen
    Commented May 1, 2012 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
    Commented May 1, 2012 at 17:51
  • @egreg Ok. But I forgot to say that it must be generic.
    – cjorssen
    Commented May 1, 2012 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
    Commented May 1, 2012 at 21:45
  • Yes, of course. One can use \AfterEndPreamble of etoolbox to ensure that the code is executed after babel activations.
    – egreg
    Commented May 1, 2012 at 21:58

2 Answers 2

8

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}
6
  • However this is not expandable and with \protect in front of it, a following active character would be expanded. :(
    – egreg
    Commented May 1, 2012 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. Commented May 1, 2012 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
    Commented May 1, 2012 at 20:16
  • 1
    answer expanded as requested Commented May 1, 2012 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
    Commented May 1, 2012 at 20:51
2

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.

3
  • Thanks for your efforts. This approach is really interesting but unusable in my case since I need something generic.
    – cjorssen
    Commented May 2, 2012 at 16:19
  • @cjorssen This is generic! Please, make an example.
    – egreg
    Commented May 2, 2012 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
    Commented May 2, 2012 at 19:57

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .