Define a control sequence after that a space matters
For the reasons that you state in the question this can't be done.
What you might have seen re xparse is discussion of whether xparse should or should not skip over spaces while looking for arguments. This makes no difference in your \foo {}
case but does make a difference in \\{}
or \\ {}
(spaces not dropped after a command symbol with a name being a single letter of catcode other than 11) or in \foo{} {}
spaces not dropped after }
so affecting arguments other than the first.
LaTeX's \@ifnextchar
which is used to look ahead for *
forms and optional []
arguments and other things is a wrapper around \futurelet
mainly to skip spaces. this means that \\[2pt]
and \\ [2pt]
are the same, unless amsmath
is loaded which changes this so that white space is significant so that common cases like
\begin{align}
zzzaa\\
[1,2] \in zzzz
\end{align}
The white space after \\
allows the [1,2]
to be taken as characters to be typeset rather than an argument to \\
(which would generate an error as it is not a legal length syntax as required by that argument).
So in short, when defining an argument parser you have a choice to discard or keep space tokens that are before any arguments that you are picking up, but you can not keep things that are not there, and there is no space token after \foo
If yo do define \foo
in a way that a following space token is significant then you would have to call it via
\def\zzz#1{#1}
\zzz{\foo} {}
or
\expandafter\foo\space{}
or some other syntax that produced a space token at that point.
Following the suggestion of David Carlisle to change the catcode of space, you may try this:
{\catcode`\ =12\relax%
\global\let\otherspace= %
\gdef\xfoosp {\foosp}}
\def\foo{\begingroup\catcode`\ =12\futurelet\nexttok\xfoo}
\def\xfoo{%
\ifx\nexttok\otherspace
\endgroup\expandafter\xfoosp
\else
\endgroup\expandafter\foonosp
\fi}
\def\foosp#1{foo with space: arg=#1}
\def\foonosp#1{foo without space: arg=#1}
You may define \foosp
and \foonosp
as you like, even with different number of arguments.
Alternatively, the first 3 lines may be replaced with
\begingroup\lccode`+=`\ %
\lowercase{\endgroup
\let\otherspace=+
\def\xfoosp+{\foosp}}
which keeps all definitions local.
By applying the #{
-notation, you can define macros whose last argument is delimited by an opening brace. Unlike with other argument delimiters that get removed when gathering arguments, TeX will leave a delimiting opening brace in place.
(Actually the mechanism isn't restricted to opening brace character tokens. You can use any token whose category code is 1 at definition time. Could as well be #\WeIrd
after \let\WeIrd={
.)
Delimited arguments can be empty.
Therefore for obtaining a control sequence token from a set of character tokens that form the name of the control sequence token in question both for defining and for calling that control sequence token, you can (by applying the #{
-notation) invent a single control sequence \name
which processes a brace delimited argument trailed by an undelimited argument (which is nested in braces). After having TeX fetch the arguments, you can have TeX whirl them around and apply \csname..\endcsname
to the argument supplied inside braces. The name of the control sequence token in question can contain space tokens as well.
\makeatletter
%
\newcommand\name{}%
\long\def\name#1#{\UD@innername{#1}}%
%
\newcommand\UD@innername[2]{%
\expandafter\UD@exchange\expandafter{\csname#2\endcsname}{#1}%
}%
%
\newcommand\UD@exchange[2]{#2#1}%
%
\makeatother
\name foo{bar}
→ expansion step 1:
\UD@innername{foo}{bar}
→ expansion step 2:
\expandafter\UD@exchange\expandafter{\csname bar\endcsname}{foo}
→ expansion step 3:
\UD@exchange{\bar}{foo}
→ expansion step 4:
foo\bar
.
In expansion contexts you would need four \expandafter
-chains for obtaining the result.
As \romannumeral
does not produce any token when encountering a non-positive number, you can add a bit of \romannumeral
-expansion in order to reduce the amount of \expandafter
-chains.
Either do \romannumeral\name0 foo{bar}
. This way only one \expandafter
-chain hitting the \romannumeral
-token is needed.
Or have the \romannumeral
-expansion "hardcoded" within the definition - this way two \expandafter
-chains are needed. The first one for obtaining the topl-level-expansion of \name
. The second one for inducing \romannumeral
-expansion.
\makeatletter
%
\newcommand\name{}%
\long\def\name#1#{\romannumeral0\UD@innername{#1}}%
%
\newcommand\UD@innername[2]{%
\expandafter\UD@exchange\expandafter{\csname#2\endcsname}{ #1}%
}%
%
\newcommand\UD@exchange[2]{#2#1}%
%
\makeatother
With such a macro you are not bound to specific definition commands:
\name{foo}
→ \foo
.
\name\newcommand{foo}
→ \newcommand\foo
.
\name\DeclareRobustCommand{foo}
→ \DeclareRobustCommand\foo
.
\name\global\long\outer\def{foo}
→ \global\long\outer\def\foo
.
\name\expandafter{foo}\bar
→ \expandafter\foo\bar
.
\name\let{foo}=\bar
→ \let\foo=\bar
.
\name\string{foo}
→ \string\foo
.
\name\meaning{foo}
→ \meaning\foo
.
You can as well use such a macro for defining/calling macros whose names contain spaces:
\name{foo }
→ \foo␣
.
\name\newcommand{foo }
→ \newcommand\foo␣
.
\name\DeclareRobustCommand{foo }
→ \DeclareRobustCommand\foo␣
.
\name\global\long\outer\def{foo }
→ \global\long\outer\def\foo␣
.
\name\expandafter{foo }\bar
→ \expandafter\foo␣\bar
.
\name\let{foo }=\bar
→ \let\foo␣=\bar
.
\name\string{foo }
→ \string\foo␣
.
\name\meaning{foo }
→ \meaning\foo␣
.
You can also nest the calls of \name
:
Example 1:
\name\name\expandafter{f o o }{b a r }
Processing the first \name
yields:
\name\expandafter\f␣o␣o␣{b a r }
.
Processing the second \name
yields:
\expandafter\f␣o␣o␣\b␣a␣r␣
.
(Analogously: \name\name\let{f o o }={b a r }
→ \let\f␣o␣o␣=\b␣a␣r␣
.)
Example 2:
\name\name\name\expandafter\expandafter\expandafter{f o o }\expandafter{b a r }{c r a z y }
Processing the first \name
yields:
\name\name\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter{b a r }{c r a z y }
.
Processing the second \name
yields:
\name\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣{c r a z y }
.
Processing the third \name
yields:
\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣\c␣r␣a␣z␣y␣
.
Example 3:
In expansion contexts you can use \romannumeral
-expansion in order to keep things going.
\romannumeral\name\name\name0 \expandafter\expandafter\expandafter{f o o }\expandafter{b a r }{c r a z y }
\romannumeral
keeps expanding until it has found some number. In the end it will find the number0
while with non-positive numbers \romannumeral
will not deliver any token:
%\romannumneral-expansion in progress
\name\name\name0 \expandafter\expandafter\expandafter{f o o }\expandafter{b a r }{c r a z y }
Processing the first \name
yields:
%\romannumneral-expansion in progress
\name\name0 \expandafter\expandafter\expandafter\f␣o␣o␣\expandafter{b a r }{c r a z y }
.
Processing the second \name
yields:
%\romannumneral-expansion in progress
\name0 \expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣{c r a z y }
.
Processing the third \name
yields:
%\romannumneral-expansion in progress
0 \expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣\c␣r␣a␣z␣y␣
.
Now \romannumeral
finds the number 0
. Therefore \romannumeral
-expansion gets aborted and \romannumeral
won't deliver any token:
\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣\c␣r␣a␣z␣y␣
.
Be aware that \name
internally applies \csname
while applying \csname
as a side effect yields assigning the control sequence in question the meaning of the \relax
-primitive in case the control sequence in question was undefined before applying \csname
. That assignment will be restricted to the current scope even if the \globaldefs
-parameter had a positive value at the time of applying \csname
.
%%\errorcontextlines=1000
\documentclass[a4paper]{article}
\usepackage{textcomp}%
\makeatletter
\newcommand\name{}%
\long\def\name#1#{\romannumeral0\UD@innername{#1}}%
\newcommand\UD@innername[2]{%
\expandafter\UD@exchange\expandafter{\csname#2\endcsname}{ #1}%
}%
\newcommand\UD@exchange[2]{#2#1}%
\makeatother
%\newcommand\foo[2]{%
\name\newcommand{foo}[2]{%
\noindent
Control sequence whose name does not contain any space.\\
Argument 1: \textit{\textlangle#1\textrangle}\\
Argument 2: \textit{\textlangle#2\textrangle}\\
}%
\name\newcommand{foo }[2]{%
\noindent
Control sequence whose name has a trailing space.\\
Argument 1: \textit{\textlangle#1\textrangle}\\
Argument 2: \textit{\textlangle#2\textrangle}\\
}%
\name\newcommand{ f o o }[2]{%
\noindent
Control sequence whose name is interspersed with spaces.\\
Argument 1: \textit{\textlangle#1\textrangle}\\
Argument 2: \textit{\textlangle#2\textrangle}\\
}%
\begin{document}
\name{foo}{Arg 1}{Arg 2}
\name{foo }{Arg 1}{Arg 2}
\name{ f o o }{Arg 1}{Arg 2}
Nesting \texttt{\string\name}:
\name\expandafter\newcommand\expandafter*\expandafter{C o N f u SiO n}\expandafter{%
\romannumeral\name\name\name0 %
\expandafter\expandafter\expandafter{F O O}\expandafter{B A R}{C R A Z Y}%
}%
\texttt{\name\string{C o N f u SiO n} is \name\meaning{C o N f u SiO n}}%
\\
Playing around with grouping:
%Be aware that \texttt itself opens up a new scope for typesetting its argument.
%\globaldefs=1\relax
\texttt{%
\begingroup\name\string{w e i r d } is \name\endgroup\meaning{w e i r d }%
}%
\texttt{%
\name\string{w e i r d } is \name\meaning{w e i r d }%
}%
\end{document}