define a macro with two parameters delimited by optional spaces

I want to define a macro with two parameters which delimited by optional spaces. For example, from the following code

\def\mymacro #1 #2 {#1:#2 }
\mymacro one two others

I get one:two others. But if some argument is ended by a control sequence, such as

\mymacro \foo \bar others

I could not get \foo:\bar others since TeX will ignore spaces after control sequences.

How could I resolve this problem?

 Add some background: for some reasons, I need to redefine a primitive command as a macro and make it behave like the primitive command. For example, the <number> arguments of the primitive command may be of the forms 64 or "5B or \a or \foo, etc. And the spaces are optional in the sense of primitive commands.

• Is there a particular reason you feel the need to delimit arguments in this manner? It seems bound to cause further headaches. – A.Ellett Jan 21 '15 at 3:24
• You could just wrap them in parentheses: \mymacro {\foo} {\bar} others. But then, why not just define the macro as \def\mymacro#1#2{#1:#2}? – A.Ellett Jan 21 '15 at 3:27
• In what sense are the spaces supposed to be optional? If x delimits the arguments, I don't see how x can be optional regardless of what x is. – cfr Jan 21 '15 at 3:37
• @A.Ellett Actually I am hacking something and not able to modify the usages in source code except redefining some macros. – Z.H. Jan 21 '15 at 4:30
• Please, show the real problem; the solution may depend on what primitive you are dealing with. – egreg Jan 21 '15 at 8:20

While it is possible to define a macro looking for optional spaces, the primitive TeX syntax for finding a <number> carries out expansion and so it's not as easy as testing if the grabbed argument is a control sequence. Usually in these cases one uses a temporary register so that TeX is still parsing for a number (or dimen, skip, etc.) in the usual way, then use \afterassignment to pick up the value. For example, to grab two <number> arguments we might do

\catcode\@=11
\def\mymacro{%
\begingroup
\afterassignment\myacro@auxi\count0=%
}
\def\myacro@auxi{%
\afterassignment\myacro@auxii\count2=%
}
\def\myacro@auxii{%
\edef\next{\noexpand\myacro@auxiii{\the\count0}{\the\count2}}%
\expandafter\endgroup
\next
}
\def\myacro@auxiii#1#2{Values: #1 #2 }
\catcode\@=12

where I've used two scratch registers and a group to keep them 'safe'. (The same can be achieved with one register and indeed a self-redefining auxiliary, but that gets complicated!) This will then work with a range of input syntaxes

\mymacro 1 2 %
\def\foo{1}
\mymacro \foo\space 2 %
\count0=12 %
\newcount\foo
\foo=14
\mymacro\count0 \foo
\mymacro \A "88
\bye
• For \XeTeXcharclass you've got one optional =: to cover that you could remove the = in \myacro@auxi as TeX will then still allow one in assigning \count2 but not in assigning \count0 (as one is already present in the input). – Joseph Wright Jan 21 '15 at 9:14

Perhaps this can be tamed a bit. But the macro here checks its arguments to see whether they're control sequences and if they are branch one direction and if they aren't branch another. The key is the macro \@ifismacro provided by @egreg to a previous answer noted below:

\documentclass{article}

\makeatletter

%% see:  https://tex.stackexchange.com/a/21469/22413
\def\@ifismacro#1{%
\begingroup\escapechar=-1
\edef\x{\endgroup\def\noexpand\first{\string#1}}\x
\begingroup\escapechar=`\\
\edef\x{\endgroup\def\noexpand\second{\string#1}}\x
\ifnum\pdfstrcmp{\first}{\second}=\z@
\expandafter\@secondoftwo % no backslash in front
\else
\expandafter\@firstoftwo  % backslash in front
\fi}

\def\mymacro#1{%%
\def\mycontinue{}%%
\@ifismacro#1%%
{%%
\def\mycontinue{\@m@mymacro{#1}}%%
}{%%
\def\mycontinue{\@a@mymacro #1}%%
}%%
\mycontinue}

\def\@m@mymacro#1#2{%%
\def\mycontinue{}%%
\@ifismacro#2%%
{%%
\def\mycontinue{\@@mymacro {#1} {#2} }%%
}{%%
\def\mycontinue{\@a@mymacro{#1} #2}%%
}%%
\mycontinue}

\def\@a@mymacro#1 #2{%%
\def\mycontinue{}%%
\@ifismacro#2%%
{%%
\def\mycontinue{\@@mymacro {#1} {#2} }%%
}{%%
\def\mycontinue{\@@mymacro #1 #2}%%
}
\mycontinue}

\def\@@mymacro#1 #2 {%%
#1:#2 }

\makeatother

\def\foo{foo}
\def\bar{bar}

\begin{document}

\mymacro one two others

\mymacro \foo \bar others

\mymacro one \bar others

\mymacro \foo two others

\end{document}

• Note that there are various (potentially serious) limitations to this: for example, this will not parse \mymacro hello\foo something other correctly. – A.Ellett Jan 21 '15 at 5:09
• Actually the more I think about this, the more I can dream up ways in which this answer can potentially fail. I think your best bet might be to write a Perl script that can preprocess the file. Perl regexp capabilities could come in handy to clean up how certain things are written. – A.Ellett Jan 21 '15 at 5:19
• But \mymacro hello\foo something other is one of the cases which I need to handle... – Z.H. Jan 21 '15 at 6:50