# Trying to understand how \@cdr…\@nil works in relation to \expandafter

I am trying to define a macro \@ifnextchars with three arguments, the first argument being a list of characters, which expands to either the second or the third argument depending on whether one of the character of the first argument follows the command. Here follows a minimalistic example of what I have done and how I would like to use it.

\documentclass{article}

\makeatletter
\newcommand\@ifnextchars[3]{%
\expandafter\ifx\expandafter\@empty#1\@empty%
\def\@ifnextchars@tmp{#3}%
\else%
\def\@ifnextchars@tmp{%
\expandafter\expandafter\expandafter\@ifnextchars@aux\expandafter%
{\@car#1\@nil}{\@cdr#1\@nil}{#2}{#3}%
}%
\fi%
\@ifnextchars@tmp%
}
\newcommand\@ifnextchars@aux[4]{%
\expandafter\@ifnextchar#1{%
#3%
}{%
\@ifnextchars{#2}{#3}{#4}%
}%
}

\newcommand\whereinsentence{%
\@ifnextchars{.!?}{%
I am in the end of a sentence.
}{%
I am not in the end of a sentence.
}%
}
\newcommand\vowel{%
\@ifnextchars{aeiouy}{%
Vowel:
}{%
Not vowel:
}%
}
\makeatother

\begin{document}

Here is \whereinsentence a sentence\whereinsentence.

\vowel a.

\vowel b.

\end{document}


Unfortunately, it doesn’t compile:

Runaway argument?
{I am in the end of a sentence. }{\@ifnextchars {\@cdr \@cdr .!?\@nil \ETC.
! Paragraph ended before \@cdr was complete.
\par
l.44


My guess is that \@cdr didn’t expand when needed. I find this strange because I made sure to expand my \@cdr with \expandafter… So I guess that I do not understand well how \expandafter works ☹ Can someone help me?

I was inspired by this post: Generalize \@ifnextchar to consider more than one character. However, this last post was about creating a variant of \@ifnextchar looking for entire word. I am just interested in knowing whether one of the given characters are present directly afterwards.

I have made this \ifx yielding a \def to make sure that the \@ifnextchar does not react with the \fi. I do not find this very satisfying: if anyone has a better solution, I am welcoming it with pleasure ☺

• You probably want to look at \@addpunct defined in amsthm.sty. You're expanding \@car, but not \@cdr. – egreg Jul 23 '16 at 19:56
• Thanks @egreg ☺ The example I gave was more a minimal example than what I really wanted to do, so \@addpunct is of few aid for what I am trying to do. But thanks ☺ – Martin Bodin Jul 25 '16 at 7:49

You cannot expand the second argument like that. Here's a simple way with the help of eTeX:

\documentclass{article}

\makeatletter
\newcommand\@ifnextchars[3]{%
\if\relax\detokenize{#1}\relax
\def\@ifnextchars@tmp{#3}%
\else
\edef\@ifnextchars@tmp{%
\noexpand\@ifnextchars@aux
{\unexpanded\expandafter{\@car#1\@nil}}
{\unexpanded\expandafter{\@cdr#1\@nil}}
\unexpanded{{#2}{#3}}%
}%
\fi
\@ifnextchars@tmp
}
\newcommand\@ifnextchars@aux[4]{%
\@ifnextchar{#1}{%
#3%
}{%
\@ifnextchars{#2}{#3}{#4}%
}%
}

\newcommand\whereinsentence{%
\@ifnextchars{.!?}{%
I am in the end of a sentence.
}{%
I am not in the end of a sentence.
}%
}
\newcommand\vowel{%
\@ifnextchars{aeiouy}{%
Vowel:
}{%
Not vowel:
}%
}
\makeatother

\begin{document}

Here is \whereinsentence a sentence\whereinsentence.

\vowel a.

\vowel b.

\end{document}

• To be safe, you should define the command with \protected\def\@ifnextchars#1#2#3 or with \newrobustcmd from etoolbox package, or just go with \DeclareRobustCommand, but I would go for the first two options. – Manuel Jul 23 '16 at 19:55

The main problem is that you are expanding \@car, but not \@cdr, because the chain of \expandafters doesn't reach it.

Here's an implementation in expl3 that you may enjoy studying.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\cs_new_protected:Nn \bodin_ifnext_chars:nnnn
{
\tl_if_in:nnTF { #1 } { #2 } { #3 } { #4 } #2
}

\NewDocumentCommand{\ifnextchars}{mmmm}
{
\bodin_ifnext_chars:nnnn { #1 } { #4 } { #2 } { #3 }
}
\ExplSyntaxOff

\newcommand\whereinsentence{%
\ifnextchars{.!?}{%
I am in the end of a sentence.''
}{%
I am not in the end of a sentence.''
}%
}
\newcommand\vowel{%
\ifnextchars{aeiouy}{%
Vowel:
}{%
Not vowel:
}%
}

\begin{document}

Here is \whereinsentence a sentence\whereinsentence.

\vowel a.

\vowel b.

\end{document}

• I am afraid that I don’t understand something about the semantics of \expandafter, then :-\ I thought that \expandafter\expandafter\expandafter\a expandafter\b\c first unfolds \c, then \b, then \a. Is because of the curly braces that it breaks? I was not aware about expl3. When I will have time, I shall definitely look for it ☺ Thanks! – Martin Bodin Jul 25 '16 at 7:53
• @MartinBodin \expandafter eventually acts on a single token, not on a braced group. – egreg Jul 25 '16 at 7:54
• Oh. I see. Then I guess that it would work if I make local definitions of the \@car and \@cdr. Is it right? I am going to test as soon as I can. Thanks ☺ – Martin Bodin Jul 25 '16 at 12:46