I can offer a \romannumeral-expansion-driven tail-recursive loop \UD@ExtractFirstArgLoop and—as a wrapper for that loop—a macro \ExtractFirstArg.
Internally a delimiter UD@SelDOm is used. But it is used in a way only where that delimiter occurring in the argument of \ExtractFirstArg/\UD@ExtractFirstArgLoop is not a problem as occurrences of the delimiter only increase the amount of iterations performed by the \romannumeral-expansion-driven loop.
(I decided to use a sequence of character-tokens as delimiter instead of something with control-word-tokens because somebody could define a control-word-token in terms of \outer...)
As the loop is driven by \romannumeral-expansion, you obtain the result by triggering two expansion-steps.
You can, e.g., do
\unexpanded\expandafter\expandafter\expandafter{\ExtractFirstArg{{first}more stuff}}
for obtaining something like \unexpanded{first}.
The gist of the mechanism is:
Append a sequence {}⟨Delimiter⟩ to the argument passed by the user, then recursively apply a "removal-macro" with parameter text #1#2⟨Delimiter⟩ which delivers {#1} until only a single non-delimited argument is left.
(In the example below, the removal-macro is called \UD@RemoveTillUD@SelDOm and the ⟨Delimiter⟩ is UD@SelDOm.)
( {} before ⟨Delimiter⟩ for ensuring that the removal-macro finds a non-delimited argument even in case of the argument passed by the user being empty or blank (blank = consisting of explicit space tokens only). )
If the user passes, e.g., the sequence
{1}{2}{3}, then after appending you have the sequence
{1}{2}{3}{}⟨Delimiter⟩.
Applying the removal-macro with its first non-delimited and its second delimited argument yields that the removal macro's first argument will be 1 and the removal-macro's second argument will be {2}{3}{}. When the removal-macro delivers its first argument wrapped in curly braces, you have the sequence {1} - the condition of there being only a single non-delimited argument is now fulfilled.
If the user passes, e.g., the sequence
{1}{2}{3}⟨Delimiter⟩{4}, then after appending you have the sequence
{1}{2}{3}⟨Delimiter⟩{4}{}⟨Delimiter⟩.
Applying the removal-macro with its first non-delimited and its second delimited argument yields that the removal-macro's first argument will be 1 and the removal-macro's second argument will be {2}{3}. When the removal-macro delivers its first argument wrapped in curly braces, you have the sequence {1}{4}{}⟨Delimiter⟩ - the condition of there being only a single non-delimited argument is not fulfilled yet, thus do another iteration with the removal-macro:
Applying the removal-macro with its first non-delimited and its second delimited argument yields that the removal-macro's first argument will be 1 and the removal-macro's second argument will be {4}{}. When the removal-macro delivers its first argument wrapped in curly braces, you have the sequence {1} - the condition of there being only a single non-delimited argument is now fulfilled.
If the user passes an empty argument, then after appending you have the sequence
{}⟨Delimiter⟩.
Applying the removal-macro with its first non-delimited and its second delimited argument yields that {} will be taken for the removal-macro's first argument and therefore the removal-macro's first argument will be empty. The removal-macro's second argument will be empty, too, because there are no other tokens before ⟨Delimiter⟩. When the removal-macro delivers its first argument wrapped in curly braces, you have the sequence {} - the condition of there being only a single non-delimited argument is now fulfilled.
Using this mechanism, you cannot distinguish by "looking" at the result of \ExtractFirstArg the case of the argument passed by the user being empty or blank (blank = consisting of explicit space-tokens only) from the case of the argument passed by the user having a first component which is an empty argument.
I.e., the results of \ExtractFirstArg{} and \ExtractFirstArg{ } will be the same as the result of, e.g., \ExtractFirstArg{{}More stuff}: In all these cases \ExtractFirstArg will not return any token at all.
But you can easily distinguish these cases by checking the emptiness/blankness of the argument passed by the user before applying \ExtractFirstArg to it.
For checking the emptiness of an argument you can, e.g., use the \UD@CheckWhetherNull-macro of the example below.
For checking the "emptiness or blankness" of an argument you can, e.g., use the same macro when doing something which takes into account that TeX discards space-tokens that precede non-delimited arguments—just do something like:
\expandafter\UD@CheckWhetherNull\expandafter{\@firstoftwo#1{}.}%
{<#1 is empty or blank (blank = consists only of explicit space-tokens).>}%
{<#1 is not empty and not blank (but consists of something else than only explicit space-tokens).>}%
For checking the "blankness" put the pieces together:
\UD@CheckWhetherNull{#1}{%
<#1 is not blank (but empty).>
}{%
\expandafter\UD@CheckWhetherNull\expandafter{\@firstoftwo#1{}.}%
{<#1 is blank (blank = consists only of explicit space-tokens).>}%
{<#1 is not blank and not empty (and consists of something else than only explicit space-tokens).>}%
}%
\documentclass{article}
\makeatletter
%%-----------------------------------------------------------------------------
%% 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>
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral0\expandafter\@secondoftwo\string{\expandafter
\@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
\@secondoftwo\string}\@firstoftwo\expandafter{} \@secondoftwo}%
{\@firstoftwo\expandafter{} \@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Extract first inner non-delimited argument - the last thing must be
%% the sequence "{}UD@SelDOm" :
%% \romannumeral0\UD@ExtractFirstArgLoop{ABCDE{}UD@SelDOm} yields A
%% \romannumeral0\UD@ExtractFirstArgLoop{{AB}CDE{}UD@SelDOm} yields AB
%%-----------------------------------------------------------------------------
\newcommand\UD@RemoveTillUD@SelDOm{}%
\long\def\UD@RemoveTillUD@SelDOm#1#2UD@SelDOm{{#1}}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\@firstoftwo{}#1}%
{\@firstoftwo\expandafter{} \@secondoftwo{}#1}%
{\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
%%-----------------------------------------------------------------------------
%% A wrapper around the \romannumeral-expansion-based loop:
%%
%% \ExtractFirstArg{<non-delimited argument>}
%%
%% In case <non-delimited argument> is empty or blank: delivers nothing.
%% Otherwise delivers the first non-delimited argument of <non-delimited
%% argument> with surrounding curly braces removed if present.
%% The result is delivered after triggering two expansion-steps/by
%% hitting \ExtractFirstArg with \expandafter twice.
%%.............................................................................
\newcommand\ExtractFirstArg[1]{%
\romannumeral0\UD@ExtractFirstArgLoop{#1{}UD@SelDOm}%
}%
\makeatother
\parindent=0ex
\parskip=\baselineskip
\topsep=0ex
\partopsep=0ex
\pagestyle{empty}
\begin{document}
\enlargethispage{2in}%
\null\kern-1.5in
\makeatletter
\verb|\ExtractFirstArg{ABCDE}|
yields:
\ExtractFirstArg{ABCDE}
\verb|\ExtractFirstArg{{AB}CDE}|
yields:
\ExtractFirstArg{{AB}CDE}
The argument can contain the sequence \verb|UD@SelDOm| although it is used as delimiter in some place,
this just increases the amount of iterations:
\verb|\ExtractFirstArg{ABUD@SelDOmCUD@SelDOmDE}|
yields:
\ExtractFirstArg{ABUD@SelDOmCUD@SelDOmDE}
\verb|\ExtractFirstArg{{AB}CUD@SelDOmDUD@SelDOmE}|
yields:
\ExtractFirstArg{{AB}CUD@SelDOmDUD@SelDOmE}
\verb|\ExtractFirstArg{UD@SelDOmABUD@SelDOmCUD@SelDOmDE}|
yields:
\ExtractFirstArg{UD@SelDOmABUD@SelDOmCUD@SelDOmDE}
The result is delivered by triggering two expansion-steps:
\begin{verbatim}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\temp
\expandafter\expandafter\expandafter{%
\ExtractFirstArg{ABCDE}%
}
\end{verbatim}
yields:
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\temp
\expandafter\expandafter\expandafter{%
\ExtractFirstArg{ABCDE}%
}
\texttt{\string\temp: \meaning\temp}%
\begin{verbatim}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\temp
\expandafter\expandafter\expandafter{%
\ExtractFirstArg{{AB}CDE}%
}
\end{verbatim}
yields:
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\temp
\expandafter\expandafter\expandafter{%
\ExtractFirstArg{{AB}CDE}%
}
\texttt{\string\temp: \meaning\temp}%
\begin{verbatim}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\temp
\expandafter\expandafter\expandafter{%
\ExtractFirstArg{{AB}CUD@SelDOmDUD@SelDOmE}%
}
\end{verbatim}
yields:
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\temp
\expandafter\expandafter\expandafter{%
\ExtractFirstArg{{AB}CUD@SelDOmDUD@SelDOmE}%
}
\texttt{\string\temp: \meaning\temp}%
\begin{verbatim}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\temp
\expandafter\expandafter\expandafter{%
\ExtractFirstArg{UD@SelDOm{AB}CUD@SelDOmDUD@SelDOmE}%
}
\end{verbatim}
yields:
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\temp
\expandafter\expandafter\expandafter{%
\ExtractFirstArg{UD@SelDOm{AB}CUD@SelDOmDUD@SelDOmE}%
}
\texttt{\string\temp: \meaning\temp}%
\begin{verbatim}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\temp
\expandafter\expandafter\expandafter{%
\ExtractFirstArg{}%
}
\end{verbatim}
yields:
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\temp
\expandafter\expandafter\expandafter{%
\ExtractFirstArg{}%
}
\texttt{\string\temp: \meaning\temp}%
\end{document}
