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.

  • A macro cannot receive an outer token as an argument: TeX will raise an error. – Joseph Wright May 25 '12 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 '12 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. – Bruno Le Floch Jul 17 '12 at 16:04
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.

  • Thakns! But the test will be true when \def\bye{outer macro:->}. – Qing Lee May 25 '12 at 21:03
  • @QingLee Yes: I don't think one can do much about this. – egreg May 25 '12 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.... – David Carlisle May 26 '12 at 9:57
  • @DavidCarlisle yes. – Bruno Le Floch May 26 '12 at 23:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.