3

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.
<to be read again> 
                   \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 ☺

Thanks in advance.

  • 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
5

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}
  • 1
    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
2

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.