2

I am trying to create a reliable command that turns the first character of it's input to uppercase. So far I have no success, especially since it needs to work when the inputstring contains commands itself. Most notably, my attempts fail when the command contains itself. Appearently, the following does not work:

\documentclass{scrartcl}
\usepackage{xstring}
\usepackage{etoolbox}
\begin{document}

\newcommand{\singleupper}[1]{\uppercase{\StrLeft{#1}{1}}\StrGobbleLeft{#1}{1}}
\newcommand{\optcap}[2]{\ifstrequal{cap}{#2}{\singleupper{#1}}{#1}}
\optcap{\optcap{asdf}{}}{cap}
\end{document}

resulting in:

! Undefined control sequence. \@xs@StrLeft@@ ...\@xs@arg@ii {#2}\edef \@xs@call {\noexpand \@testopt {\noe...

How can I do this properly an why is my attempt failing?

edit

I have seen some nice answers all in some sense involving a secondary language (latex3, lua...) is there no fully expandable method for this in latex2e (possibly using packages)?

Fictional
  • 369
  • It is trying to change the character to the upper-case equivalent, but there is none for \StrLeft .... That's the next thing TeX sees, so that's what it feeds to \uppercase. At least, I think so. TeX doesn't work from the inside out. It works from left to right. – cfr Jan 29 '18 at 04:32
  • 1
    In fact, something like \StrLeft{\StrLeft{asdf}{3}}{1} fails with basically the same error. See section 3.2 of the xstring documentation: The macros of this package are not purely expandable, i.e. they cannot be put in the argument of an \edef. Nestling macros is not possible neither. (You can also see the alternatives proposed.) – ShreevatsaR Jan 29 '18 at 07:30
  • Is the first character alway ascii? – Ulrike Fischer Jan 29 '18 at 11:21

2 Answers2

1

Do you want something like this?

\documentclass[border=10pt]{standalone}
\usepackage{xparse}
\begin{document}
\ExplSyntaxOn
\NewDocumentCommand \optcap { +m +m }
{
  \tl_if_eq:nnTF { #2 } { cap }
  {
    \tl_mixed_case:n { #1 }
  }{
    #1
  }
}
\ExplSyntaxOff
\optcap{asdf}{} 
\optcap{\optcap{asdf}{}}{cap}
\end{document}

optional upper-casing

Note that if you put the N/Y in, then nesting the result will mean the added character is the first, I think, which doesn't seem to be what you wanted. But I'm not sure I've understood, so let me know if I should delete this.

cfr
  • 198,882
  • It appears this requires the l3package and l3kernel packages. The N/Y is just for debugging purposes. I will remove them from the question to reduce confusion. This is indeed part of an answer as it explains how to achieve what I need. However, I would also like to know why my approach fails and how this one works. – Fictional Jan 29 '18 at 05:56
  • @Fictional the expl3 case changing commands are expandable (so for example you could use them in \typeout) the xstring commands do not work by expansion and use internal assignments. and the \uppercase primitive is not expandable either. Also \uppercase is not really suitable for natural language text unless you only consider English. – David Carlisle Jan 29 '18 at 07:56
  • How do I know which commands are expandable and which are not? If you add this as well as the above and the required packages, I will accept your answer... – Fictional Jan 29 '18 at 17:01
  • @Fictional If who does that you'll accept whose answer? You can use texdef, say, to look at macros' definitions. You could use \token_if_expandable:NTF if you just want to know yes or no. But you should ask David - not me. – cfr Jan 29 '18 at 23:11
  • @cfr, I meant if you add it to your answer. However, I would like to wait a little longer to see if a more 'pure' latex2e answer shows up. – Fictional Jan 31 '18 at 10:50
  • @Fictional But it is not my comment. – cfr Feb 01 '18 at 00:31
  • I see... My bad. – Fictional Feb 02 '18 at 07:42
  • @Fictional The LaTeX 3 stuff is compatible with LaTeX 2e. You don't need to compile differently. You should wait for somebody to offer something using more familiar syntax, if you want that, though. – cfr Feb 02 '18 at 23:18
1

If you're willing to use LuaTeX (i.e. compile with lualatex instead of pdflatex), then you might be able to save yourself some macro-expansion headaches. For example, you can put the following in foo.lua:

-- The string s, with its first character converted to uppercase
function firstUpper(s)
   first = unicode.utf8.upper(unicode.utf8.sub(s, 1, 1))
   rest = unicode.utf8.sub(s, 2, -1)
   return first .. rest
end

function optCap(s, opt)
   if opt == 'cap' then
      return firstUpper(s)
   else
      return s
   end
end

and your .tex file can be:

\documentclass{scrartcl}

\directlua{dofile('foo.lua')}
% \newcommand{\firstUpper}[1]{\directlua{tex.print(firstUpper('#1'))}}
\newcommand{\optcap}[2]{\directlua{tex.print(optCap('#1', '#2'))}}

\begin{document}
\optcap{asdf}{}

\optcap{asdf}{cap}

\optcap{\optcap{asdf}{}}{cap}
\end{document}

output

(This may fail if you give complicated macros to it, but I consider that a feature.)

As for why your original attempt fails, this is documented in section 3.2 of the xstring documentation:

The macros of this package are not purely expandable, i.e. they cannot be put in the argument of an \edef. Nestling macros is not possible neither.

To understand what “expandable” means here, you can read answers on this site like this, this, this and this. Basically not all of the operation of \StrLeft (etc.) happens in the “mouth” (input expansion processor / syntactic routines); it expands to some non-expandable commands that need to be processed in the “stomach” (execution processor / semantic routines). If you really want to do it with macros you can use the “expandable” ones from expl3, as in @cfr's answer.

ShreevatsaR
  • 45,428
  • 10
  • 117
  • 149