6

I'm in need of a macro which receives a control sequence as argument to test if this control sequence is actually an \outer macro. I can code it myself as below .

\documentclass{minimal}
\usepackage{expl3}
\ExplSyntaxOn
\group_begin:
\char_set_lccode:nn { `\; } { `\: }
\char_set_lccode:nn { `\Z } { `\t }
\char_set_lccode:nn { `\T } { `\T }
\char_set_lccode:nn { `\F } { `\F }
\tl_map_function:nN { O U Z E R M A C R ; } \char_set_catcode_other:N
\tl_to_lowercase:n
  {
    \group_end:
    \prg_new_conditional:Nnn \cs_if_outer_macro:c { p, T , F , TF }
      {
        \exp_last_unbraced:Nf \cs_if_outer_macro_aux:w
        \cs_meaning:c {#1} OUZER ~ MACRO ; \q_stop
      }
    \cs_new_nopar:Npn \cs_if_outer_macro_aux:w #1 OUZER ~ MACRO ; #2 \q_stop
      {
        \prg_case_str:xxn { #1 ~ }
          {
            { \token_to_str:N \ }
            { \prg_return_true: }
            { \token_to_str:N \long \token_to_str:N \ }
            { \prg_return_true: }
            { \token_to_str:N \protected \token_to_str:N \ }
            { \prg_return_true: }
            { \token_to_str:N \protected \token_to_str:N \long \token_to_str:N \ }
            { \prg_return_true: }
          }
          { \prg_return_false: }
      }
  }
\cs_new_eq:NN \IfOuterMacroTF \cs_if_outer_macro:cTF
\ExplSyntaxOff

\long\outer\protected\def\fooa{}
\outer\def\foob{}
\def\fooc{}
\def\food{outer macro:}

\typeout{It's \IfOuterMacroTF{fooa}{}{NOT }an outer macro.}
\typeout{It's \IfOuterMacroTF{foob}{}{NOT }an outer macro.}
\typeout{It's \IfOuterMacroTF{fooc}{}{NOT }an outer macro.}
\typeout{It's \IfOuterMacroTF{food}{}{NOT }an outer macro.}
\typeout{It's \IfOuterMacroTF{relax}{}{NOT }an outer macro.}

It is inspired by the l3token package. The test is expandable. I have used it in the xeCJK package. But, it only receives the csname instead of control sequence. What is more, I think it is a bit slow.

I think it is a good question and like to see other possible approaches.

3
  • A macro cannot receive an outer token as an argument: TeX will raise an error.
    – Joseph Wright
    May 25, 2012 at 18:18
  • @MarcoDaniel I meant you can't do \outer\def\foo{}\def\baz#1{}\baz\foo, i.e. pass an outer token as an argument.
    – Joseph Wright
    May 25, 2012 at 18:53
  • @QingLee: for completeness about \outer tokens, you also have to be careful about \foo defined by \halign{#\cr\global\futurelet\foo\relax\cr}. Its \meaning is \outer endtemplate: and it indeed is outer. Jul 17, 2012 at 16:04

1 Answer 1

5

This is not expandable, but maybe the test can be modified to be:

\documentclass{article}
\usepackage{expl3,l3regex}

\ExplSyntaxOn
\cs_new_protected:Npn \isouter { \exp_after:wN \checkouter:w \token_to_meaning:N }
\cs_new:Npn \checkouter:w #1 \relax
  {
   \regex_match:nnTF { outer .* macro .* :-> } { #1 } 
    { \typeout{OUTER~MACRO} } 
    { \typeout{NON~OUTER~TOKEN} }
  }
\ExplSyntaxOff

\outer\def\bye{bye}

\isouter\bye\relax

\stop

However this test can't be used in arguments to other macros, because of the rule mentioned by Joseph Wright.

4
  • Thakns! But the test will be true when \def\bye{outer macro:->}.
    – Qing Lee
    May 25, 2012 at 21:03
  • @QingLee Yes: I don't think one can do much about this.
    – egreg
    May 25, 2012 at 21:05
  • does Bruno support ^ to anchor the regexp to start of string, in which case you could use that as the trick case would have meaning macro:->outer macro.... May 26, 2012 at 9:57
  • @DavidCarlisle yes. May 26, 2012 at 23:15

You must log in to answer this question.

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