# Assigning a value from xstring

I'm trying to write some basic validation of user input to a command \noinsuit which takes two parameters, an integer in [0, 13] and a letter indicating the suit.

So I expect \noinsuit{1}{C} to be valid and both \noinsuit{Q}{C} and \noinsuit{1}{Q} to be invalid. That's working fine, but I'm having a problem using xstring's \StrLeft{#1}{1} to test if the first character is an integer. The following MWE prints its value correctly but if I uncomment the test line it throws

! Undefined control sequence.
\xs_StrLeft__ ...ef \xs_arg_ii {#2}\edef \xs_call
{\noexpand \xs_testopt {\n...


A problem with the expansion I suppose.

\documentclass{article}

\usepackage{xparse}
\usepackage{xstring}

\NewDocumentCommand\noinsuit{mm}{%
\renewcommand{\test}{\StrLeft{#1}{1}}
%   \IfInteger{\test}{\renewcommand{\singpl}{cards}}{[\PackageError{BridgeStyle : \noinsuit}{Invalid integer (#1)}{}]}%
\IfEqCase{#1}{%
{1}{\renewcommand{\singpl}{card}}%
}
\IfEqCase{#2}{%
{D}{#1 \singpl{} in Diamonds}%
{H}{#1 \singpl{} in Hearts}%
}[\PackageError{BridgeStyle : \noinsuit}{Unknown suit (#2)}{}]%
}%

\begin{document}

\noinsuit{1}{C}

\noinsuit{2}{C}

%    \noinsuit{Q}{H}

%    \noinsuit{3}{Q}

\end{document}

• This seems to work fine for me. When I uncomment the last two lines I get Package BridgeStyle : \noinsuit Error: Unknown suit (Q) which seems to be the correct result as Q is not a known suite. Perhaps one of your packages is out of date? Also, if you want to save the value from an xstring macro, just use the optional last parameter. So instead of \renewcommand{\test}{\StrLeft{#1}{1}} use \StrLeft{#1}{1}[\test]. – Peter Grill Feb 20 at 9:20
• @Peter Grill Thanks for that - saving the value as you suggest gets my live code working! If you post that as an answer I'll accept it. – DLyons Feb 20 at 9:45
• egreg's answer already covers that and more... – Peter Grill Feb 20 at 20:28

You can't use \StrLeft inside the argument to \IfInteger. You're passing the instructions to build a string rather than the string.

Rather do

  \StrLeft{#1}{1}[\test]
\IfInteger{\test}
{\renewcommand{\singpl}{cards}}
{\PackageError{BridgeStyle : \noinsuit}{Invalid integer (#1)}{}}%


On the other hand, you can get more control by using expl3.

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand\noinsuit{mm}
{
\bridgestyle_noinsuit_checknumber:nn { #1 } { #2 }
}

\cs_new_protected:Nn \bridgestyle_noinsuit_checknumber:nn
{
\regex_match:nnTF { \A [0-9]{1,2} \Z } { #1 }
{% we have one or two digits
\int_compare:nTF { #1 <= 13 }
{% the integer is between 0 and 13
\bridgestyle_noinsuit_checksuit:nn { #1 } { #2 }
}
{% error
\bridgestyle_noinsuit_invalid:nn { integer } { #1 }
}
}
{% invalid input for the integer
\bridgestyle_noinsuit_invalid:nn { integer } { #1 }
}
}

\cs_new_protected:Nn \bridgestyle_noinsuit_checksuit:nn
{
\regex_match:nnTF { \A [CDHS] \Z } { #2 }
{% valid suit
#1~card\int_compare:nF { #1=1 } {s}~in~\bridgestyle_suit:n { #2 }
}
{% invalid suit
\bridgestyle_noinsuit_invalid:nn { suit } { #2 }
}
}

\cs_new:Nn \bridgestyle_suit:n
{
\str_case:nn { #1 }
{
{C}{Clubs}
{D}{Diamonds}
{H}{Hearts}
}
}

\cs_new_protected:Nn \bridgestyle_noinsuit_invalid:nn
{
\PackageError{bridgestyle}{(\string\noinsuit)~invalid~#1~(#2)}{}
}
\ExplSyntaxOff

\begin{document}

\noinsuit{0}{D}

\noinsuit{1}{C}

\noinsuit{2}{C}

\noinsuit{Q}{H}

\noinsuit{3}{Q}

\noinsuit{14}{H}

\end{document}


The console output is

! Package bridgestyle Error: (\noinsuit) invalid integer (Q).

See the bridgestyle package documentation for explanation.
Type  H <return>  for immediate help.
...

l.65 \noinsuit{Q}{H}

?

! Package bridgestyle Error: (\noinsuit) invalid suit (Q).

See the bridgestyle package documentation for explanation.
Type  H <return>  for immediate help.
...

l.67 \noinsuit{3}{Q}

?

! Package bridgestyle Error: (\noinsuit) invalid integer (14).

See the bridgestyle package documentation for explanation.
Type  H <return>  for immediate help.
...

l.69 \noinsuit{14}{H}

?


The printed output is