How keep macro with \csname from gobbling up starred argument?

For purposes more involved than what's shown here, I want to test certain commands having both an ordinary form and a starred form, with the latter constructed using the suffix package. To do that, I pass each form as argument into a macro \try that involves a \csname...\endcsname expression.

For example:

\documentclass{article}

\usepackage{suffix}

\newcommand{\toward}{\rightarrow}
\WithSuffix\newcommand\toward*{\Rightarrow}

\newcommand{\try}[1]{$a \csname#1\endcsname b$}

\begin{document}

OK: $a \toward b$ and $a \toward* b$

OK---\verb!\try{toward}! works: \try{toward}

BAD--\verb!\try{toward*}! gobbles up the arrow: \try{toward*}

\end{document}


As shown below, the result of a \Rightarrow b disappears from the output of \try{toward*}

How can that be fixed?

The problem is that \toward* is two tokens, not one, but \csname toward*\endcsname only builds one token.

You can emulate \csname...\endcsname with \scantokens:

\documentclass{article}

\usepackage{suffix}

\newcommand{\toward}{\rightarrow}
\WithSuffix\newcommand\toward*{\Rightarrow}

\makeatletter
\edef\bschar{\expandafter\@gobble\string\\}
\makeatother

\newcommand{\try}[1]{%
$a% \scantokens\expandafter{\bschar#1}% b$%
}

\begin{document}

OK: $a \toward b$ and $a \toward* b$

OK---\verb!\try{toward}! works: \try{toward}

OK---\verb!\try{toward*}! gobbles up the arrow: \try{toward*}

\end{document}


A different solution that provides for an expandable \try and also avoids suffix.

\documentclass{article}
\usepackage{xparse}

\NewDocumentCommand{\toward}{s}{%
\IfBooleanTF{#1}{\Rightarrow}{\rightarrow}%
}

\ExplSyntaxOn
\NewExpandableDocumentCommand{\try}{m}
{
\str_if_eq:eeTF
{ \str_range:nnn { #1 } { -1 } { -1 } } % get the last character
{ * }
{ $a \use:c { \str_range:nnn { #1 } { 1 } { -2 } } * b$ }
{ $a \use:c { #1 } b$ }
}
\ExplSyntaxOff

\begin{document}

OK: $a \toward b$ and $a \toward* b$

OK---\verb!\try{toward}! works: \try{toward}

OK---\verb!\try{toward*}! works: \try{toward*}

\end{document}


The argument to \try is stringified and examined: if the last item in the string is *, then we detach it and put after the command.

• maybe simpler if OP defined its \try to itself have a starred variant so \try{toward} vs \try*{toward}, the latter testing effect of \toward*. – user4686 Dec 15 '18 at 10:33
• @jfbu I guess that the OP wants to show the effect of some macros with the minimum effort. – egreg Dec 15 '18 at 10:37
• (off-topic) a bit disappointing that suffix package allows only single token suffixes – user4686 Dec 15 '18 at 10:53
• Although this method complicates the code for \try, it localizes the additional work there rather than requiring (as in @Werner's answer ) additional definitions for the commands to be fed as arguments to\try. Thank you both for the solution and the straightforward explanation of what was wrong. – murray Dec 15 '18 at 15:38
• @egreg: Whare at the advantages of the method that uses xparse instead of suffix? (It looks like using xparse would involve learning & applying a whole little additional syntax & semantics.) – murray Dec 15 '18 at 19:43

The result of \try{toward*} disappears because there is not command \csname toward*\endcsname. And \csname...\endcsname for an undefined command turns into \relax (which prints nothing).

If you want to use \try{toward*} but also have \toward* work as a regular starred macro, then you can just define \csname toward*\endcsname to be the same as \toward* (or \Rightarrow):

\documentclass{article}

\usepackage{suffix}

\newcommand{\toward}{\rightarrow}
\WithSuffix\newcommand\toward*{\Rightarrow}% This defines \toward to have a starred version
\expandafter\newcommand\csname toward*\endcsname{\Rightarrow}% This defines \towards* ...
% ... which is different
% from what is defined above

\newcommand{\try}[1]{$a \csname#1\endcsname b$}

\begin{document}

OK: $a \toward b$ and $a \toward* b$

OK---\verb!\try{toward}! works: \try{toward}

OK--\verb!\try{toward*}! gobbles up the arrow: \try{toward*}

\end{document}


Something related from The TeXbook (p 40):

... you can go from a list of character tokens to a control sequence by saying '\csname<tokens>\endcsname'. The tokens that appear in this construction between \csname and \endcsname may include other control sequences, as long as those control sequences ultimately expand into characters instead of TeX primitives; the final characters can be of any category, not necessarily letters. For example, '\csname TeX\endcsname' is essentially the same as '\TeX'; but '\csname\TeX\endcsname' is illegal, because \TeX expands into tokens containing the \kern primitive. Furthermore, '\csname\string\TeX\endcsname' will produce the unusual control sequence '\\TeX', i.e., the token \TeX, which you can't ordinarily write.

• This method works just fine. Its only drawback is that it requires additional coding for each starred command to be fed as argument to \try. (In the actual application, the \try takes multiple arguments, several of which have both ordinary and starred forms.) – murray Dec 15 '18 at 15:40
• I especially appreciate the explanation that \csname...\endcsname for an undefined command turns into \relax. – murray Dec 15 '18 at 15:53
• Be aware that the undefined command is turned into \relax only within the current scope. This assignment is restricted to the global scope even when the \globaldefs-parameter has a positive value. – Ulrich Diez Dec 15 '18 at 16:21

In case you wish to do without eTeX and it is only about single trailing stars as suffixes, I can offer a macro \UD@SplitTrailingStar.

Syntax: \UD@SplitTrailingStar{⟨sequence⟩}{⟨prepend⟩}{⟨append⟩}

In case ⟨sequence⟩ doesn't have a star as last token yields:
⟨prepend⟩⟨sequence⟩⟨append⟩

In case ⟨sequence⟩ does have a star as last token yields:
⟨prepend⟩⟨sequence without the last star⟩⟨append⟩*

⟨prepend⟩ can, e.g., be \csname.

⟨append⟩ can, e.g., be \endcsname.

Due to \romannumeral-expansion, the result is delivered after two expansion-steps.

\documentclass{article}

\usepackage{suffix}

\makeatletter
%%----------------------------------------------------------------------
%% Paraphernalia
%%----------------------------------------------------------------------
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@Exchange[2]{#2#1}%
%%----------------------------------------------------------------------
%% 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:
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
\UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%----------------------------------------------------------------------
%% \UD@SplitTrailingStarLoop{<sequence>*}{}{}{<prepend>}{<append>}
%% In case <sequence> doesn't have a star as last token yields:
%%   <prepend><sequence><append>
%% In case <sequence> does have a star as last token yields:
%%   <prepend><sequence without the last star><append>*
%%----------------------------------------------------------------------
\newcommand\UD@GobbleToExclam{}\long\def\UD@GobbleToExclam#1!{}%
\newcommand\UD@CheckWhetherNoExclam[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@GobbleToExclam#1!}%
}%
\newcommand\UD@gobbletostar{}\long\def\UD@gobbletostar#1*{}%
\newcommand\UD@keeptostar{}\long\def\UD@keeptostar#1*{#1}%
\newcommand\UD@RemoveStarDelimitedTillUD@nil{}%
\long\def\UD@RemoveStarDelimitedTillUD@nil#1*#2\UD@nil{#1*}%
\newcommand\UD@ExtractFirstStarDelimitedLoop[2]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@gobbletostar#1}%
{\expandafter\expandafter\expandafter\UD@Exchange
\expandafter\expandafter\expandafter{%
\expandafter\UD@firstoftwo\expandafter{\expandafter}%
\UD@keeptostar#1}{ #2}}%
{%
\expandafter\UD@ExtractFirstStarDelimitedLoop
\expandafter{\UD@RemoveStarDelimitedTillUD@nil#1}{#2}%
}%
}%
\newcommand\UD@starfork{}\long\def\UD@starfork#1!!*!#2#3!!!!{#2}%
\newcommand\UD@SplitTrailingStarLoop[5]{%
\UD@CheckWhetherNoExclam{#1}{%
\UD@starfork!#1!*!{\UD@firstoftwo{#4#2#5}}% #1 empty.
!!#1!{\UD@firstoftwo{#4#2#5*}}% #1 single star.
!!*!{\UD@secondoftwo{}}%
!!!!%
}{\UD@secondoftwo{}}{%
\expandafter\expandafter\expandafter\UD@Exchange
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\expandafter{%
\expandafter\UD@Exchange\expandafter{%
\romannumeral0\UD@ExtractFirstStarDelimitedLoop{.#1\UD@nil}{}}{#2#3}}}%
{\expandafter\UD@SplitTrailingStarLoop
\expandafter{\UD@gobbletostar#1}}%
{*}{#4}{#5}%
}%
}%
%%----------------------------------------------------------------------
%% \UD@SplitTrailingStar{<sequence>}{<prepend>}{<append>}
%% In case <sequence> doesn't have a star as last token yields:
%%   <prepend><sequence><append>
%% In case <sequence> does have a star as last token yields:
%%   <prepend><sequence without the last star><append>*
%%
%% Due to \romannumeral-expansion, the result is delivered after two
%% expansion-steps.
%% \UD@SplitTrailingStar is a wrapper for \UD@SplitTrailingStarLoop.
%%----------------------------------------------------------------------
\newcommand\UD@SplitTrailingStar[2]{%
\romannumeral0\UD@SplitTrailingStarLoop{#1*}{}{}{ #2}%
}%

\newcommand{\try}[1]{$a\UD@SplitTrailingStar{#1}{\csname}{\endcsname}b$}%

\newcommand{\toward}{\rightarrow}
\WithSuffix\newcommand\toward*{\Rightarrow}

\makeatother

\parskip=\baselineskip
\parindent=0ex

\begin{document}

\verb!$a\csname toward\endcsname b$!: $a\csname toward\endcsname b$

\verb!$a\csname toward\endcsname*b$!: $a\csname toward\endcsname*b$

\verb!\try{toward}!: \try{toward}

\verb!\try{toward*}!: \try{toward*}

\hrulefill

% something more complex:

\makeatletter
\newcommand\name{}\long\def\name#1#{\romannumeral0\innername{#1}}%
\newcommand\innername[2]{%
\expandafter\UD@Exchange\expandafter{\csname#2\endcsname}{ #1}%
}%
\makeatother

\name\newcommand{*{ma}*ny**star}{\rightarrow}
\name\WithSuffix\newcommand{*{ma}*ny**star}*{\Rightarrow}

\verb!$a\csname*{ma}*ny**star\endcsname b$!: $a\csname*{ma}*ny**star\endcsname b$

\verb!$a\csname*{ma}*ny**star\endcsname*b$!: $a\csname*{ma}*ny**star\endcsname*b$

\verb!\try{*{ma}*ny**star}!: \try{*{ma}*ny**star}

\verb!\try{*{ma}*ny**star*}!: \try{*{ma}*ny**star*}

\hrulefill

\name\newcommand{*{mo}*re**stars*}{\rightarrow}
\name\WithSuffix\newcommand{*{mo}*re**stars*}*{\Rightarrow}

\verb!$a\csname*{mo}*re**stars*\endcsname b$!: $a\csname*{mo}*re**stars*\endcsname b$

\verb!$a\csname*{mo}*re**stars*\endcsname*b$!: $a\csname*{mo}*re**stars*\endcsname*b$

\verb!\try{*{mo}*re**stars**}!: \try{*{mo}*re**stars**}

But Problem:

\verb!\try{*{mo}*re**stars*}!: \try{*{mo}*re**stars*}

Reason:

\verb|\UD@SplitTrailingStarLoop| cannot "know" that this time the trailing star should not be treated as
a suffix but as a part of the macro-name.

\verb!\try{*{mo}*re**stars*}!\\
yields:\\
\verb!$a\csname*{mo}*re**stars\endcsname*b$!\\
while the control-word-token \verb|\*{mo}*re**stars| at the time of performing \verb|\csname|-expansion
is undefined and thus within the current scope gets assigned the meaning of the \verb|\relax|-primitive.

\end{document}


• This approach seems evidently more complicated (at least to the extent that the complication of methods using suffix or xparse don't just hide such complication inside their packages). – murray Dec 15 '18 at 20:36
• The approach might seem complicated because it involves expandable checking without introducing forbidden "sentinel tokens" and without whatsoever TeX-extensions whether the last token inside a macro-argument is a star * while due to \csname..\endcsname control-sequence-tokens' names can contain several stars which implies the need of expandably detecting the last one if present without loosing surrounding curly braces if present... – Ulrich Diez Dec 16 '18 at 0:50