Philosophically speaking/as a moot point one could raise the question whether you also need tests for finding out whether
- a given control sequence token is already defined.
- a given control sequence name denotes a control sequence token which is already defined.
If eTeX extensions are available, one can use the primitives \ifdefined
for the further and \ifcsname
for the latter.
If they are not available, a macro \@ifundefined
can be implemented as in older releases of the LaTeX2e kernel which does form the control sequence token from the \csname..\endcsname
construct and then does check whether the meaning of that token does equal the meaning of the \relax
-primitive.
(\csname..\endcsname
does within the current scope (even if \globaldefs
is positive it does so only in the current scope) assign the control sequence token in question the meaning of the \relax
-primitive in case it is undefined.)
\newcommand\@ifundefined[1]{%
\expandafter\ifx\csname#1\endcsname\relax
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
}
With the older-LaTeX2e-kernel-approach undefined control sequences will within the current scope be defined to \relax
after testing the first time on them. With the older-LaTeX2e-kernel-approach one cannot distinguish cases where control sequences were already defined to \relax
before testing took place from cases where control sequences weren't defined at all before testing took place.
By introducing some trickery with grouping/scoping, it is possible both to avoid permanently assigning within the current scope the meaning of the \relax
-primitive to undefined control sequences and to distinguish the above mentioned cases:
\documentclass{article}
\begingroup
\makeatletter
\@firstofone{%
\endgroup
\newcommand\old@ifundefined[1]{%
\expandafter\ifx\csname #1\endcsname\relax
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
}%
\newcommand\CheckWhetherUndefined[1]{%
\begingroup
\expandafter\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter\endgroup
\old@ifundefined{#1}%
{% -> defined equal to \relax or undefined
\begingroup
\expandafter\expandafter\expandafter\endgroup
\old@ifundefined{#1}{%
% -> defined to equal \relax
\@secondoftwo
}{%
% -> undefined
\@firstoftwo
}%
}{% -> defined
\@secondoftwo
}%
}%
}%
\begin{document}
\makeatletter
\ttfamily\selectfont
\def\foo{defined}
\let\bar=\relax
\verb|\meaning\foo| $\to$ \meaning\foo
\verb|\meaning\bar| $\to$ \meaning\bar
\verb|\meaning\UnDeFINed| $\to$ \meaning\UnDeFINed
Test with \string\CheckWhetherUndefined:
\verb|\CheckWhetherUndefined{foo}{undefined}{defined}| $\to$
\CheckWhetherUndefined{foo}{undefined}{defined}
\verb|\CheckWhetherUndefined{bar}{undefined}{defined}| $\to$
\CheckWhetherUndefined{bar}{undefined}{defined}
\verb|\CheckWhetherUndefined{UnDeFINed}{undefined}{defined}| $\to$
\CheckWhetherUndefined{UnDeFINed}{undefined}{defined}
Test with \LaTeXe's \string\old@ifundefined:
\verb|\old@ifundefined{foo}{undefined}{defined}| $\to$
\old@ifundefined{foo}{undefined}{defined}
\verb|\old@ifundefined{bar}{undefined}{defined}| $\to$
\old@ifundefined{bar}{undefined}{defined}
\verb|\old@ifundefined{UnDeFINed}{undefined}{defined}| $\to$
\old@ifundefined{UnDeFINed}{undefined}{defined}
\end{document}

But there are still some issues left:
If you wish to go the \old@ifundefined
/\CheckWhetherUndefined
-route with control sequence tokens also instead of given names, you need to derive the names from the control sequence tokens. You can do so by using \string
and removing the leading escape character if present. In order to find out whether a leading escape character is present and if so, whether that character is a character token of category code 12 (other) or a space token, you can evaluate the current value of the integer parameter \escapechar
.
Alternatively it can be ensured that no leading escape character will be present by (within a local scope) assigning \escapechar
a negative value.
But this does not take into account that somebody might like to apply the test on an active character token.
Besides this, tests where grouping/introducing local scopes and assigning values to integer parameters like \escapechar
is needed, do not work in expansion contexts, as grouping/scoping and assigning does not take place at the same time when expansion takes place as expansion and grouping/scoping/assigning do take place in different sections of (La)TeX' digestive apparatus.
One also needs to keep in mind that implementing whatsoever tests does usually take place in terms of macros while macros do process things argument-wise and thus not necessarily token-wise.
How to catch up scenarios where more than one token is passed as an argument?
How to catch up scenarios where the first token of the argument is an opening brace of catcode 1?
How to catch up scenarios where a (single) non-control-sequence-token is passed as an argument?
How to catch up scenarios where the argument is empty?
How to catch up scenarios where the test should not be about a control sequence token but about an active character token?
Probably a fully expandable test without eTeX-extensions would be nice for finding out whether the very first token of the argument is something that is undefined.
I've implemented such a test in the example below.
The gist of the test is:
First a dot-character and thus something that is not undefined is attached to make sure that the argument is not empty.
Then it is expandably checked whether the first token of the argument is an opening brace/an explicit catcode-1-character token.
If so, it is obvious that with that argument there is no first token being something undefined.
Otherwise that first token of the argument can be "hit" via \meaning
, and after obtaining the result of that "hitting" checking can take place on whether now the argument has the leading phrase "undefined" consisting of characters of category code 12(other):
\documentclass{article}
\makeatletter
%%----------------------------------------------------------------------
%% Paraphernalia:
%%----------------------------------------------------------------------
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@Exchange[2]{#2#1}%
%%----------------------------------------------------------------------
%% Expandably within two expansion steps check whether argument is empty:
%%----------------------------------------------------------------------
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
%%
%% A concern in his posting is that the argument is hit with \string
%% after some expansions which in edge cases might result in unbalancing
%% surrounding \if..\fi-constructs if the macro is used inside of such
%% \if..\fi-constructs.
%%
%% That challenging concern sickened me. ;-)
%%
%% Therefore I decided to implerment a variant where this cannot happen
%% as expansion is forced by \romannumeral:
%%
%% After the first expansion-step, \string is not applied yet.
%% After the second expansion-step, any possibly disturbing remainders
%% are already removed due to \romannumeral-expansion.
%%
%% No eTeX- or whatsoever extensions. No \if.. .Only \romannumeral,
%% digit 0, space token for terminating \romannumeral-expansion,
%% \string, \expandafter, \UD@firstoftwo, \UD@secondoftwo, {, }.
%%
%% May 20, 2016
%%
%% Ulrich Diez (e-mail: ud.usenetcorrespondence@web.de)
%%......................................................................
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
\UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%----------------------------------------------------------------------
%% Expandably within two expansion steps check whether argument's first
%% token is a catcode-1-character
%%----------------------------------------------------------------------
%% \UD@CheckWhetherLeadingBrace{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked has leading
%% catcode-1-token>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked has no leading
%% catcode-1-token>}%
%%......................................................................
\newcommand\UD@CheckWhetherLeadingBrace[1]{%
\romannumeral0\expandafter\UD@secondoftwo\expandafter{\expandafter{%
\string#1.}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
\UD@firstoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@secondoftwo}%
}%
%%----------------------------------------------------------------------
%% Expandably within two expansion steps check whether macro argument
%% has an undefined token as its first token:
%%----------------------------------------------------------------------
%% \CheckWhetherArgumentHasAFirstTokenThatIsUndefined%
%% {<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked does have an
%% undefined token as its first token.>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked does not have an
%% undefined token as its first token.>}%
%%......................................................................
\begingroup
\newcommand\CheckWhetherArgumentHasAFirstTokenThatIsUndefined[1]{%
\endgroup
\newcommand\CheckWhetherArgumentHasAFirstTokenThatIsUndefined[1]{%
\romannumeral0\UD@CheckWhetherLeadingBrace{##1}%
{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@secondoftwo}%
{\expandafter\UD@secondoftwo\string{\expandafter
\CheckWhetherArgumentHasAFirstTokenThatIsUndefinedB
\meaning##1.#1}{}}%
}%
\newcommand\CheckWhetherArgumentHasAFirstTokenThatIsUndefinedB{}%
\long\def\CheckWhetherArgumentHasAFirstTokenThatIsUndefinedB##1#1{%
\UD@CheckWhetherNull{##1}%
{\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
{\UD@Exchange{ }{\expandafter\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter}\expandafter\expandafter
\expandafter}\expandafter\UD@secondoftwo\expandafter{\string}%
}%
}%
\begingroup
\edef\CheckWhetherArgumentHasAFirstTokenThatIsUndefined{%
\endgroup
\noexpand\CheckWhetherArgumentHasAFirstTokenThatIsUndefined{%
\string u\string n\string d\string e\string f%
\string i\string n\string e\string d%
}%
}%
\CheckWhetherArgumentHasAFirstTokenThatIsUndefined
%%----------------------------------------------------------------------
%% A macro for those who prefer nesting \if-conditionals:
%% (Be aware that after reading the closing brace behind
%% <Argument which is to be checked> TeX' reading-apparatus will be
%% in state M (middle of line) and therefore you need to use
%% comment-characters for avoiding unwanted spaces!!!)
%%----------------------------------------------------------------------
%% \if\ArgumentHasAFirstTokenThatIsUndefined{<Argument which is to be checked>}%
%% <Tokens to be delivered in case that argument
%% which is to be checked does have an
%% undefined token as its first token.>
%% \else
%% <Tokens to be delivered in case that argument
%% which is to be checked does not have an
%% undefined token as its first token.>
%% \fi
%%......................................................................
\newcommand\ArgumentHasAFirstTokenThatIsUndefined[1]{%
0\CheckWhetherArgumentHasAFirstTokenThatIsUndefined{#1}{0}{1}%
}%
\makeatother
\begin{document}
\parindent=0ex
\parskip=\baselineskip
\footnotesize
\nonfrenchspacing
\ttfamily\selectfont
Testing \string\CheckWhetherArgumentHasAFirstTokenThatIsUndefined
\verb|\CheckWhetherArgumentHasAFirstTokenThatIsUndefined{a}{true}{false}| $\to$\\
\CheckWhetherArgumentHasAFirstTokenThatIsUndefined{a}{true}{false}
\verb|\CheckWhetherArgumentHasAFirstTokenThatIsUndefined{\TeX}{true}{false}| $\to$\\
\CheckWhetherArgumentHasAFirstTokenThatIsUndefined{\TeX}{true}{false}
\verb|\CheckWhetherArgumentHasAFirstTokenThatIsUndefined{\uNdEfInED}{true}{false}| $\to$\\
\CheckWhetherArgumentHasAFirstTokenThatIsUndefined{\uNdEfInED}{true}{false}
\verb|\CheckWhetherArgumentHasAFirstTokenThatIsUndefined{a\biZarre\fi}{true}{false}| $\to$\\
\CheckWhetherArgumentHasAFirstTokenThatIsUndefined{a\biZarre\fi}{true}{false}
\verb|\CheckWhetherArgumentHasAFirstTokenThatIsUndefined{\TeX\biZarre\fi}{true}{false}| $\to$\\
\CheckWhetherArgumentHasAFirstTokenThatIsUndefined{\TeX\biZarre\fi}{true}{false}
\verb|\CheckWhetherArgumentHasAFirstTokenThatIsUndefined{\uNdEfInED\biZarre\fi}{true}{false}| $\to$\\
\CheckWhetherArgumentHasAFirstTokenThatIsUndefined{\uNdEfInED\biZarre\fi}{true}{false}
\verb|\CheckWhetherArgumentHasAFirstTokenThatIsUndefined{{in braces}}{true}{false}| $\to$\\
\CheckWhetherArgumentHasAFirstTokenThatIsUndefined{{in braces}}{true}{false}
\verb|\CheckWhetherArgumentHasAFirstTokenThatIsUndefined{}{true}{false}| $\to$\\
\CheckWhetherArgumentHasAFirstTokenThatIsUndefined{}{true}{false}
Testing \string\if\string\ArgumentHasAFirstTokenThatIsUndefined
\verb|\if\ArgumentHasAFirstTokenThatIsUndefined{a}true\else false\fi| $\to$\\
\if\ArgumentHasAFirstTokenThatIsUndefined{a}true\else false\fi
\verb|\if\ArgumentHasAFirstTokenThatIsUndefined{\TeX}true\else false\fi| $\to$\\
\if\ArgumentHasAFirstTokenThatIsUndefined{\TeX}true\else false\fi
\verb|\if\ArgumentHasAFirstTokenThatIsUndefined{\uNdEfInED}true\else false\fi| $\to$\\
\if\ArgumentHasAFirstTokenThatIsUndefined{\uNdEfInED}true\else false\fi
\verb|\if\ArgumentHasAFirstTokenThatIsUndefined{a\biZarre\fi}true\else false\fi| $\to$\\
\if\ArgumentHasAFirstTokenThatIsUndefined{a\biZarre\fi}true\else false\fi
\verb|\if\ArgumentHasAFirstTokenThatIsUndefined{\TeX\biZarre\fi}true\else false\fi| $\to$\\
\if\ArgumentHasAFirstTokenThatIsUndefined{\TeX\biZarre\fi}true\else false\fi
\verb|\if\ArgumentHasAFirstTokenThatIsUndefined{\uNdEfInED\biZarre\fi}true\else false\fi| $\to$\\
\if\ArgumentHasAFirstTokenThatIsUndefined{\uNdEfInED\biZarre\fi}true\else false\fi
\verb|\if\ArgumentHasAFirstTokenThatIsUndefined{{in braces}}true\else false\fi| $\to$\\
\if\ArgumentHasAFirstTokenThatIsUndefined{{in braces}}true\else false\fi
\verb|\if\ArgumentHasAFirstTokenThatIsUndefined{}true\else false\fi| $\to$\\
\if\ArgumentHasAFirstTokenThatIsUndefined{}true\else false\fi
\end{document}

\operatorname{Var}
, for instance.\everymath
and an\everydisplay
that scan the whole math expression for undefined control sequences. I was hoping that LuaTeX would provide a callback to suppress an undefined control sequence error, but it doesn't seem so.\def\foo{\baz}
…;-)
\baz
.