# Detecting \global, \long, \outer, \protected

Is there a way to detect whether or not the next assignment is going to be \global, \long, \outer, or \protected? That is, is it possible to write a command \detectmodifiers such that code like

\def\otherdef{%
\detectmodifiers
preprocessing
\ifglobal\expandafter\global\fi
\iflong\expandafter\long\fi
\ifouter\expandafter\outer\fi
\ifprotected\expandafter\protected\fi
\def
}


does the right thing (defines an \otherdef command that acts the same way as \def, but does some preprocessing first)?

• The question is not very clear. What kind of preprocessing do you have in mind? – egreg Apr 12 '11 at 18:12
• @egrep: He wants to detect if his \otherdef was preceded by a \global etc. modifier. So when the user is writing \global\otherdef\macro{<code>} it can pass the \global to the underlying \def while having some code before that. – Martin Scharrer Apr 12 '11 at 18:31
• not very sure if I understood the logic behind your question. If not what you are after pls post a comment and I will clarify or delete my post. – Yiannis Lazarides Apr 12 '11 at 18:41
• I'm not really clear on where the 'underlying \def' comes from. The TeX modifiers all allow expansion, so it is possible to have things lit \def\example{\let\a\b} and have \global\example work correctly. – Joseph Wright Jul 19 '11 at 16:02
• The kind of preprocessing I had in mind included defining other commands. In particular, I'm playing with writing some code that localizes \global to a particular group (so that {\let\foo=a {\localizeglobal {\global\let\foo=b}\show\foo }\show\foo} gives b for the first show and a for the second show.). So I want to be able to play around with defining other macros before a given \def is run. (Though I may end up deciding that the easiest solution is to parse the arguments to \def (or \otherdef), execute \def first, and then use \let as I need to.) – Jason Gross Dec 31 '11 at 12:58

You can make a dummy \def inside \detectmodifiers which will be affected by any of the mentioned modifiers if its at the beginning of the macro. Then you can test for the modifiers to build the if-switches. You can use \meaning on a dummy macro which contains the \outer, \long and \protected modifiers as string which can be extracted and turned into macros again using \scantokens. One challenge would be \global: you need to make the assignment in a group and check if it is still the same after the group.

You should use the if-tree you have but rather define a macro \themodifiers which is then put in front of \def:
\def\themodifiers{\protected\global} ... \themodifiers\def....

Here some proof-of-concept code. I remembered that the "modifiers" are called prefixes, so I changed the macro names. The prefixes are stored in the \theprefixes macro which can be used in front of \def. The \global prefix however is special and can't be read like this. I tried to define the \dummy macro inside a group and use \global\let\gdummy\dummy so that I can test if both are still the same after the end of the group. The problem is: a \begingroup or { isn't allowed before the \def\dummy :-(

You should change the names of some macros to prevent name clashes.

\documentclass{article}

\makeatletter
\def\macroother{macro}
\@onelevel@sanitize\macroother
\makeatother

\expandafter
\def\expandafter\returnprefixes\expandafter#\expandafter1\macroother#2\relax{%
\scantokens{\def\theprefixes{#1}}%
}

\def\detectprefixes{%
\def\dummy{}%
\expandafter\returnprefixes\meaning\dummy\relax
}

\def\otherdef{%
\detectprefixes
% other code
\theprefixes\def
}

\global\long\outer\protected\otherdef\mymacro{mystuff}
\show\mymacro

\begin{document}

\end{document}

• I think that the only way to detect \global is to redefine \global to do something like \detectprefixes\globaltrue\afterassignment\globalfalse\global\theprefixes. The problem is that this breaks \afterassignment. To repair this, we need to redefine \afterassignment to store its token using \futurelet\afterassignmenttoken\afterassignmentaux, then have \afterassignmentaux emit a true \afterassignment (so that it still works for other assignments). We also don't want the \dummy definition to trigger \afterassignment... – Bruno Le Floch Apr 12 '11 at 21:16

You can detect the differences by building a series of conditionals.

\def\Z{This}
\global\def\X{This}
\outer\def\Y{This}

\ifx\Z\Y true \else false \fi


The \ifx will only return true if the two tokens are macros, and they both have the same status with respect to \long and \outer, and they both have the same parameters and "top level" expansion. Based on the conditional you can the build the command using csname.

• \Y is not allowed inside a macro because it is \outer. Maybe \csname Y\endcsname will work? – Martin Scharrer Apr 12 '11 at 18:44
• @Martin I was thinking something as \modifier\macrofactorymodifier\macroname\definition. This would call a command macrofactory after the group who does the checks against macrofactorymodifier and defines the command. macrofactory should be outer and long. I am failing to see the benefits though of defining the command straight or passing a flag. – Yiannis Lazarides Apr 12 '11 at 18:56
• One benefit of this is that you can have complex definition macros like \newcommand which do a lot before they finally define the macro, but still can use the normal prefixes/modifiers in front of them. – Martin Scharrer Apr 12 '11 at 19:12

Idea: Instead of writing a command that does some funny stuff to detect whether \global or another prefix came before, modify \global etc. to look ahead and use a protocol to inform the appropriate macro that it is to consider itself prefixed.

The following is not working code and is intended to suggest an approach

\let\ea\expandafter
\makeatletter

\let\@global\global
\let\@long\long
% etc
\let\@def\def
\let\@edef\edef
% etc
\let\@let\let

\@def\@prefixables{%
\def%
\edef%
% etc
\let%
}

\@def\newprefixable#1{
\@def\prefixable@def@reset{
\ea\@def\csname prefixable@#1@global\endcsname{0}
\ea\@def\csname prefixable@#1@long\endcsname{0}
% etc
}
\@def\prefixable@def@reset
\@edef\@prefixables{%
\expandonce{\@prefixables}%
\expandonce{\csname#1\endcsname}%
}
}

\def\global{%
% Pseudocode
If the next token is in \@prefixables:
\@def\prefixable@<the token>@global{1}
End if
}

\newprefixable{def}
% Showing only treatment of \global
\@def\def#1#2{%
\if\prefixable@def@global1%
\@global\@def#1#2%
\else%
\@def#1#2%
\fi%
\prefixable@def@reset%
}
\newprefixable\otherdef % The macro in the question

\makeatother


Additional considerations: It needs to work if more than one prefix comes before a command, e.g. if something like \long\global\otherdef happens. This isn't handled by the above

• This will break almost every usage of \global: try \global\dimen1=1cm, no matter how you redefine \global. See Bruno' comment to Martin's answer; and note that the syntax rules say that \global triggers expansion of the following token. – egreg Apr 28 '15 at 8:53