9

I would like to define a command that will behave according to the nature of the last character fed it. But I'm having difficulty just getting the components of my command to work.

I thought I could grab the last letter of the string that I'm passing the command and use expl magic to make it upper case. Then I would test whether the last letter and its upper case version are the same. But I can't even get that far.

In this MWE, I've defined two commands. The first command is just supposed to grab the first token and change it to upper case. It fails to do this. the second is supposed to reverse the string and return only the first token. That too is failing.

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn

\tl_new:N \l_ae_lasttoken_tl
\tl_new:N \l_ae_upper_lasttoken_tl
\NewDocumentCommand{\aeupcasefirstelement}{ m }
    {
        \tl_set:Nn  \l_ae_lasttoken_tl        { \tl_head:n { #1 } }
        \tl_set:Nn  \l_ae_upper_lasttoken_tl  { \tl_to_uppercase:n {  \l_ae_lasttoken_tl } }
        %% show results of these actions
        \textbf{ \l_ae_lasttoken_tl {~   vs.  ~} \l_ae_upper_lasttoken_tl }
    }

\NewDocumentCommand{\aelastelement}{ m }
    {
        \tl_set:Nn \l_ae_lasttoken_tl { \tl_head:V { \tl_reverse:V #1 } }
        %% show results of these actions
        \textbf{ \l_ae_lasttoken_tl }
    }

\ExplSyntaxOff
\pagestyle{empty}
\setlength{\parindent}{0pt}
\begin{document}

\begin{tabular}{ll}
Expecting to see & Getting \\
\textbf{T vs. T} & \aeupcasefirstelement{This is a test of my command} \\
\textbf{t vs. T} & \aeupcasefirstelement{this is a test of my command} \\
\textbf{d}       & \aelastelement{This is a test of my command}
\end{tabular}

\end{document}
  • You want to expand the contents of the brace groups, the n argument type doesn't do that. If you change your tl_set:Nn's to \tl_set:Nx's, then it looks like your problem might be solved. – Scott H. May 30 '13 at 0:10
  • Whether or not that fixes your problem, as a side note, the show commands are really useful for diagnosing this sort of thing. For example, after your first \tl_set:Nn command, add a \tl_show:N \l_ae_lasttoken_tl add have a look at your console. – Scott H. May 30 '13 at 0:11
  • That solves the problem with changing to upper case. But my second command still doesn't behave properly. – A.Ellett May 30 '13 at 0:11
  • thanks for the commend about \tl_show:N. that is useful! – A.Ellett May 30 '13 at 0:13
  • @ScottH. is right. I don't know about xparse but \uppercase works the same way, \MakeUppercase on the other hand is not expandable but the t will show up as T. Are you interested in xstring solutions? The \StrRight macro comes to mind. The first element can easily be extracted with plainTeX solutions. – Qrrbrbirlbel May 30 '13 at 0:13
9

This defines a conditional that tests if the last item in a token list is uppercase; the token list should consist only of ASCII characters.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\prg_new_protected_conditional:Npnn \ae_if_last_uc:n #1 { T , F , TF }
 {
  \tl_set:Nx \l_ae_testa_tl { \tl_item:nn { #1 } { -1 } }
  \tl_set:Nx \l_ae_testb_tl { \text_uppercase:V \l_ae_testa_tl }
  \tl_if_eq:NNTF \l_ae_testa_tl \l_ae_testb_tl
   { \prg_return_true: }
   { \prg_return_false: }
 }
\cs_generate_variant:Nn \text_uppercase:n { V }

% A test
\ae_if_last_uc:nTF { abc } { \typeout{UC} } { \typeout{LC} }
\ae_if_last_uc:nTF { abC } { \typeout{UC} } { \typeout{LC} }

\ExplSyntaxOff

\stop

The test outputs

LC
UC

Your problem is common: \tl_to_uppercase:n (that is, the primitive \uppercase) is not expandable.


Let's look at your macros.

\NewDocumentCommand{\aeupcasefirstelement}{ m }
 {
  \tl_set:Nn \l_ae_lasttoken_tl        { \tl_head:n { #1 } }
  \tl_set:Nn \l_ae_upper_lasttoken_tl  { \tl_to_uppercase:n {  \l_ae_lasttoken_tl } }
  %% show results of these actions
  \textbf{ \l_ae_lasttoken_tl {~   vs.  ~} \l_ae_upper_lasttoken_tl }
 }

With \aeupcasefirstelement{abc} you store in \l_ae_lasttoken_tl the tokens \tl_head:n{a} and in \l_ae_upper_lasttoken_tl the tokens

\tl_to_uppercase:n{\l_ae_lasttoken_tl}

which is surely not what you want. The fact that the printed result seems to be correct is purely incidental, because during typesetting TeX expands and executes tokens. In order to store the first item in \l_ae_lasttoken_tl you need to expand the function \tl_item:nn, so use

\tl_set:Nx \l_ae_lasttoken_tl { \tl_item:nn { #1 } }

would be the correct idiom. For uppercasing, you have to resort to \text_uppercase:n as I did above, or do the uppercasing in a different and indirect fashion:

\exp_args:Nx \tl_to_uppercase:n
 {
  \exp_not:n { \tl_set:Nn \l_ae_lasttoken_uppercase_tl } { \l_ae_lasttoken_tl }
 }

(defining a variant \tl_to_uppercase:x would be better, of course).

The biggest problem is that \tl_to_uppercase:n is not expandable and does its work in a curious way. Also note that \tl_to_uppercase:n no longer exists in expl3, as its oddity and the way it works are not in par with the scope of expl3 code.

| improve this answer | |
  • Nice solution. Can you explain why my approach of reversing the string and then trying to grab the head is failing to work? – A.Ellett May 30 '13 at 0:23
6

This grabs the last char and stuffs it in \lastchar. Then it uppercases that char and stuffs it in \uplastchar. Then, a simple \if will differentiate.

\documentclass{article}
\usepackage{stringstrings}
\begin{document}
\def\strng{This is a test of the emergency broadcast system}
\strng

\substring[q]{\strng}{$}{$}
\edef\lastchar{\thestring}

\caseupper[q]{\thestring}
\edef\uplastchar{\thestring}

\lastchar~\uplastchar

\if\lastchar\uplastchar SAME\else DIFFERENT\fi

\end{document}

p.s. In general, the strings cannot contain macros, though there are various exceptions and some workarounds, if necessary.

| improve this answer | |
  • Interesting approach. I was hoping to stick with something in xpl3. – A.Ellett May 30 '13 at 0:24
  • @A.Ellett Understood. – Steven B. Segletes May 30 '13 at 0:30
1

The other possibility to explore is pdfTeX primitive \pdfmatch. Macro \islastcap checks if the state of the last letter in the string, ignoring other characters.

\def\islastcap#1{%
\ifnum\pdfmatch subcount 1 {[A-Z][^a-zA-Z]*$}{#1}=1 %
[U] \else [L] \fi \ #1}

\def\str{ arl kdS   }  

\islastcap{\str}

\islastcap{\str a  }

\islastcap{\str .  }

\islastcap{\str . Y;  }

\islastcap{\str . Yn/T/f/;  }

\bye

The output:

[U] arl kdS
[L] arl kdS a
[U] arl kdS .
[U] arl kdS . Y;
[L] arl kdS . Yn/T/f/;
| improve this answer | |

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.