Fully robust way to access the first item in a token list (expandably)

If you allow some pdftex primitives I think you can do this, which uses the entire input list as marker.

\begingroup
  \catcode`@=11
  \long\gdef\firstofmany#1{%
    \@fom{\unexpanded{[#1]}}#1{[#1]}}

  \long\gdef\@fom#1#2{%
   \unexpanded{#2}%
   \@gobbleto{#1}}

\gdef\@gobbleto#1#2{%
  \ifnum\pdfstrcmp{\unexpanded{#2}}{#1}=\z@
  \expandafter\@gobbletwo
  \else
  \fi
  \@gobbleto{#1}}

\gdef\@gobbletwo#1#2{}

\endgroup



\message{"\firstofmany{\a\b\c}"} % => "\a "
\message{"\firstofmany{ { ab} c}"} % => " ab"


\bye

EDIT: Much much shorter. What was I thinking before? Or am I sleepy now? :-)

Strip the left brace using \string and \gobble, get the first item, put the brace back in.

\catcode`@=11
\def\@gobble#1{}

\def\firstofmany{\expandafter\expandafter\expandafter
                 \fom@getfirst\expandafter\@gobble\string}
\def\fom@getfirst#1{\unexpanded{#1}\fom@gobble}
\def\fom@gobble{\expandafter\expandafter\expandafter
                     \expandafter\expandafter\expandafter
                     \expandafter\@gobble\iftrue\expandafter{\else}\fi}

\message{"\firstofmany{\a\b\c}"}

\bye

Proper treatement of initial groups is missing here, though (they are found, but the braces are not written out).


I think I may have found a pure eTeX solution. I have attacked it with everything I could think of and it seems to work... except in the case of a blank list (error) and lists starting with space (space is ignored). But these were mentioned above as unimportant anyway.

I don't know about speed improvement, though --- I'm no expert, but the thing is quite complicated...

Before giving the code, a conceptual overview.

  1. The list is detokenized. (Thus, eTeX only.)

  2. In the detokenized version, the outer groups of braces are counted. (This part could be optimized by using the TeX's macro argument parsing mechanism. But at this stage, I was implementing for clarity, not speed.) The assumption is that { and } (and only these) have catcode 1 and 2, but this could be easily generalized, I believe.

    The number of outer groups is carried around as a list of *s of the appropriate length.

    Ok, this was the easy part :-)

  3. The idea is to dismantle the group using \string: the opening brace is stringified and then gobbled. The problem, however, is how to expand the \string and \gobble. Our *-based "counter" is in the way... (By the way, it seems to me completely impossible to pass the counter around (as part of argument lists) after the degrouped list, because we don't want to use a fixed delimiter.)

    Part of the solution is \let*\expandafter. We need to expand two macros after the *-counter, so we will walk through the stars twice, so 1/4 of them will remain. But when we "multiply" the counter by four, all is well. :-)

  4. After the group is dismantled, we have easy access to the first item. True, we need to be a bit careful with first items that are groups etc, but all in all, this part is more tedious that innovative.

  5. The only remaining part of magic is the gobbling. We alternate between gobbling the outer groups and the tokens between them. Since we know how many outer groups there are, we know when to stop, so we don't meet the now-lonely right brace (we eventually provide him a partner, of course).

    We gobble the tokens between outer groups using the \def\gobble...#{ trick (TeXbook p.204).

\catcode`@=11

\def\afterfi#1#2\fi{\fi#1}
% use \onefi etc after these
\def\afterfifi#1#2#3\fi#4\fi{#1#2}
\def\afterfififi#1#2#3\fi#4\fi#5\fi{#1#2}
\def\afterfifififi#1#2#3\fi#4\fi#5\fi#6\fi{#1#2}
\def\onefi{\fi}
\def\twofi{\fi\fi}
\def\threefi{\fi\fi\fi}
\def\fourfi{\fi\fi\fi\fi}
\def\gobble#1{}

\def\openingbrace{\iftrue{\else}\fi}
\def\closingbrace{\iffalse{\else}\fi}

% Detokenize (while preserving the original)
\long\def\firstofmany#1{%
  \expandafter\fom@countfirstlevelgroups\detokenize{#1}de{}{}{#1}%
}

\catcode`*=13  % we'll be counting stars
\def\if@zero\if#1#2/{%   % zero test
  \ifx#1/%
    \afterfi{\if@zero@yes}%
  \else
    \afterfi{\if@zero@no}%
  \fi
}
\def\if@zero@yes{\iftrue}
\def\if@zero@no/{\iffalse}

{\catcode`(=1 \catcode`)=2 (\catcode`{=12 \catcode`}=12

\xdef\detok@openingbrace({)%

% Count the number of outer brace pairs
%
% Note 1: This macro is very non-optimized... it should use TeX's macro
% argument parsing mechanism to search for { and }, and shouldn't use
% all these \afterfi-s, I used this approach just for clarity.
%
% Note 2: This macro expects precisely { and } to be of catcode 1 and 2.
% This could be fixed, but it's not worth the effort at this point.
%
% Args: #1#2 = detokenized, #3 = n, #4 = depth
% --> letters are safe delimiters, because \detokenize produces `other's
% We save the very first token for later (#5 below).
\gdef\fom@countfirstlevelgroups#1#2e#3#4(%  
  \fom@countfirstlevelgroups@#1#2e(#3)(#4)#1% 
)
\gdef\fom@countfirstlevelgroups@#1#2e#3#4#5(% 
  \ifx#1d% end of detokenized string
    \afterfifififi(\onefi)(\fom@removeopeningbrace#5(#3))%
  \else
    \ifx#1{% { found ==> increase depth
      \if@zero\if#4//% { found at zero depth ==> increase n 
        \afterfifififi(\threefi)(\fom@countfirstlevelgroups@#2e(#3*)(#4*)#5)%
      \else
        \afterfifififi(\threefi)(\fom@countfirstlevelgroups@#2e(#3)(#4*)#5)%
      \fi
    \else
      \ifx#1}% } found => decrease depth
        \afterfififi(\threefi)(\fom@cflg@decreasedepth#2e(#3)[#4]#5)%
      \else % neither { not } found ==> go to next char
        \afterfififi(\threefi)(\fom@countfirstlevelgroups@#2e(#3)(#4)#5)%
      \fi
    \fi
  \fi
)
\gdef\fom@cflg@decreasedepth#1e#2[#3#4]#5(%
  \fom@countfirstlevelgroups@#1e(#2)(#4)#5)

)}  % back to normal braces

% Remove the initial brace.
% *s are quadrapled to expand first \string (followed by }, we know)
% and \gobble, thus destroying the group; we will be left with the
% original number of *s
\let*\expandafter
\def\fom@removeopeningbrace#1#2{% #2=***** (n), #1=the first *token*
  \expandafter\expandafter\expandafter\fom@adddummy
  \expandafter\expandafter\expandafter#1%
  #2#2#2#2\expandafter\expandafter\expandafter e%
  \expandafter\gobble\string
}

% Insert a dummy group (and a *) after the first item. We will
% start gobbling by gobbling to a group and this would fail if there
% were none.  This needs to be done before checking for group below,
% so that we have enough *s.
\long\def\fom@adddummy#1#2e#3{%
  \fom@checkforgroup#1#2*e{#3}{}%
}

% Group as the first item requires special attention.  (Note: space
% would need it as well, but space never get here anyway: it
% dissapears when \fom@countfirstlevelgroups is expanded.)
% #1 = the first token of the detokenized first item (will be now
% finally discarded)
% #2#3 = *s (if we will find an opening brace, one * will be removed)
\def\fom@checkforgroup#1#2#3e{%
  \if\detok@openingbrace#1%
    \afterfi{\fom@havegroup#3e}%
  \else
    \afterfi{\fom@getfirstitem#2#3e}%
  \fi
}
% Put extra braces around the first item which is a group.
\long\def\fom@havegroup#1e#2{\fom@getfirstitem#1e{{#2}}}

% Get the first item, then call the gobblers: insert two markers
% instead of one, the gobblers need them.
\long\def\fom@getfirstitem#1e#2{%
  \unexpanded{#2}%
  \fom@gobbletogroup#1*ef{}%
}

% Gobble: we know how many groups we have (as many as *s), so we
% can gobble by alternating \fom@gobbletogroup...#3#{...}
% and \fom@gobblegroup...#3{...}
% #1#2=*s, #3=toks before group; but first check if there are any
% *s left!
\def\fom@gobbletogroup#1#2f{%
  \ifx#1e%
    \afterfi\fom@finish
  \else
    \afterfi{\fom@gobbletogroup@#1#2f}%
  \fi
}
\long\def\fom@gobbletogroup@#1#2f#3#{%
  \fom@gobblegroup#1#2f%
}

% #1#2=*s, #3=the group
\def\fom@gobblegroup#1#2f{%
  \ifx#1e%
    \afterfi\fom@finish
  \else
    \afterfi{\fom@gobblegroup@#1#2f}%
  \fi
}
\long\def\fom@gobblegroup@#1#2f#3{%
  \fom@gobbletogroup#2f%
}

\def\fom@finish{%
  \iftrue\expandafter\fom@finish@\expandafter{\else}\fi
}
\long\def\fom@finish@#1{}

% TEST:
\message{"\firstofmany{#1\fom@gobblegroup{\par #1  # @@@ef

aa}a**aa{first} l{ine{%
\fom@gobblegroup\fi\fi
}s}econd line} efef "}

\bye

I understand I am rather late to the party, but I would like to make a somewhat shorter suggestion than the currently extant ones. Idea: Contrive to put braces around the tail end of the list and then just \@gobble the whole thing. It fully expands to the first item, but there is an extraneous \iffalse{\fi at the beginning. Of course, there is no notion in TeX of expanding "partway", so I'm not sure whether this is an issue in practice. In any case, since it is at the beginning it can be excised in various ways :)

\documentclass{article}
\makeatletter
\def\firstofmany#1{\iffalse{\fi%
 \@firstofmany#1}%
}
\def\@firstofmany#1{%
    \unexpanded{#1}\expandafter\@gobble\expandafter{\iffalse}\fi
}
%\def\@gobble#1{}

\def\dotest#1{\edef\@dotest{\firstofmany{#1}}\meaning\@dotest.}
\makeatother
\begin{document}
\tt
\dotest{abcde}

\dotest{{ab}cde}

\dotest{ { ab}cde}

\dotest{\a\b\c}
\end{document}

enter image description here