7

There are two forms of setting the type style: the declarative method e.g, \itshape and the command form, e.g, \textit. The command form takes an argument whereas the declarative form does not --- it just applies itself to whatever comes afterwards.

I would like to develop a declarative form, say \UppercaseIt, of the command form \MakeUppercase{<text>}. I have read, in source2e.pdf on CTAN the code for implementing \textit, \itshape and \MakeUppercase and can't claim that I understand any of them. I am after something like {\UppercaseIt some text} instead of \MakeUppercase{some text}.

I only have a code outline of what I'm after, since I don't know how to do it:

\documentclass{article}

\newcommand{\UppercaseIt}{%
  %... magic
}

\begin{document}

\MakeUppercase{some text}% Yields SOME TEXT

{\UppercaseIt some text}% Should yield SOME TEXT

\end{document}
Henri Menke
  • 109,596
Peter Wilson
  • 28,066
  • 3
    By "clean" ways you cannot because of the underlying primitive behaviour. \itshape tells TeX to just change the font, while \MakeUppercase, which uses \uppercase, has to have an argument. You can do some hacking, like here, but it won't work exactly the same way – Phelype Oleinik Mar 04 '20 at 20:17
  • 3
    \textit and \itshape are two different ways of changing a font. \Uppercaseit, to be parallel with \itshape, would require that you have an all-uppercase font. – barbara beeton Mar 04 '20 at 20:17
  • @barbarabeeton Couldn't one define a "font" in which all lowercase letters get represented by their uppercase counterparts? At least theoretically this should be possible. –  Mar 04 '20 at 20:29
  • 1
    @Schrödinger'scat -- I think it's possible to construct a virtual font that's all uppercase, but I think this might raise other problems that I'm not willing to speculate about at the moment. (I'm on the way out the door for an evening at the theater,) – barbara beeton Mar 04 '20 at 21:20
  • @Schrödinger's cat with lualatex it is rather easy (and some fonts have it inbuilt https://tex.stackexchange.com/q/370799/2388. With pdflatex it is possible to but more work. – Ulrike Fischer Mar 04 '20 at 23:21
  • @UlrikeFischer I am looking forward to seeing your answer. ;-) –  Mar 04 '20 at 23:38
  • You can probably define a command \UppercaseIt which makes lowercase-characters active and defines them to expand to their uppercase-pendants. (But this will break macros which check for character tokens with specific character codes and category codes rather than just character codes. It will also not work with macros that expand to non-active lowercase character tokens. Therefore probably a mechanism is preferable where uppercasing takes place when all expansion-work is done. I think that's why people usually stick to \MakeUppercase. The textcase package might also be of interest.) – Ulrich Diez Mar 06 '20 at 11:07
  • Be aware that "declarative" commands that do not process arguments usually do not affect the way in which following things get tokenized while with commands that process arguments those arguments that are not already tokenized get tokenized at the time of carrying out these commands: With \textit{\catcode`\A=13 A} the second A is not active. With {\itshape\catcode`\A=13 A} the second A is active. – Ulrich Diez Mar 06 '20 at 11:17

4 Answers4

3

Others already said why this isn't really possible, so I won't bring more sand to this beach. Here's an implementation that fakes that behaviour by scanning ahead until the next explicit close brace, then grabbing everything and passing it to \MakeUppercase as argument.

The implementation uses expl3's new \peek_analysis_map_inline:n to read the input stream token by token, and decide what to do based on the catcode of that token. If a token of catcode 1 (begin group) is found, a counter is increased. Similarly, if a token of catcode 2 (end group) is found, that counter is decreased. If a token of catcode 2 is found, and the counter is zero, the scanning stops, the tokens collected thus far are passed as argument to \MakeUppercase (\text_uppercase:n, actually), and the final end group token found is left to TeX do its thing.

This differs wildly from font commands like \itshape that obey TeX groups. \itshape (using it as the example) will take effect until the current group is closed, or until the document ends. The group-end may be an explicit end group token like }, an implicit one like \egroup, or an actual \endgroup, or some other less common ones, and this token may be hidden inside a macro. The command defined here only works with explicit end group tokens (}), seen without expanding tokens (basically what TeX would accept as the close brace when grabbing an argument to a macro).

Here is the code:

\documentclass{article}

\usepackage{xparse} \ExplSyntaxOn \tl_new:N \l__wilson_collect_tl \int_new:N \l__wilson_nesting_int \NewDocumentCommand \UppercaseIt { } { % magic follows: \wilson_declarative_command:n { \text_uppercase:n {##1} } } \cs_new_protected:Npn \wilson_declarative_command:n #1 { \cs_set_protected:Npn __wilson_do_cmd:n ##1 {#1} \tl_clear:N \l__wilson_collect_tl \peek_analysis_map_inline:n { __wilson_analyse:nnn {##1} {##2} {##3} } } \cs_new_protected:Npn __wilson_analyse:nnn #1 #2 #3 { \if_case:w "#3 \exp_stop_f: \exp_after:wN \use_i:nn \or: \int_incr:N \l__wilson_nesting_int \exp_after:wN \use_i:nn \or: \int_compare:nNnTF { \l__wilson_nesting_int } = { 0 } { \exp_after:wN \use_ii:nn } { \int_decr:N \l__wilson_nesting_int \exp_after:wN \use_i:nn } \else: \exp_after:wN \use_i:nn \fi: { \tl_put_right:Nn \l__wilson_collect_tl {#1} } { __wilson_analyse_end:n {#1} } } \cs_new_protected:Npn __wilson_analyse_end:n #1 { \tl_set:Nx \l__wilson_collect_tl { \l__wilson_collect_tl } \peek_analysis_map_break:n { \exp_last_unbraced:NNNo \exp_args:NV __wilson_do_cmd:n \l__wilson_collect_tl #1 } } \ExplSyntaxOff

\begin{document}

\MakeUppercase{some text}% Yields SOME TEXT

{\UppercaseIt some text}% Should yield SOME TEXT

{\UppercaseIt {some} text}% Should yield SOME TEXT

\end{document}

But then, that was just to show off \peek_analysis_map_inline:n, which is really handy. With those restrictions you can get the same behaviour, with much less work by using something simple as:

\documentclass{article}

\newcommand\UppercaseIt{% \expandafter\UppercaseItAux \expandafter{\iffalse}\fi} \newcommand\UppercaseItAux[1]{% \def\uppercasetmpa{\MakeUppercase{#1}}% \expandafter\uppercasetmpa\iffalse{\fi}}

\begin{document}

\MakeUppercase{some text}% Yields SOME TEXT

{\UppercaseIt some text}% Should yield SOME TEXT

{\UppercaseIt {some} text}% Should yield SOME TEXT

\end{document}

  • Thank you. Your "simple" answer works for my use-case {\font some text} where I want to change \font to uppercasing some text. Any thoughts of creating a package for doing this sort of thing? (At times I want to lowercase some text, and similar declarative version of commands that take an argument. – Peter Wilson Sep 23 '21 at 17:44
  • @PeterWilson Sorry, no intention to make a package out of that, and unfortunately not much time either nowadays. But feel free to use the code as you wish. I don't intend to make it a package because this approach pretends to be something it actually isn't, then users will stumble upon all kinds of weird interactions (varbatim commands, for example, won't work well with \UppercaseIt), so I don't see it as something to be broadly encouraged, rather as a TeX programming trick – Phelype Oleinik Sep 24 '21 at 01:33
  • I appreciate your packaging thoughts. I admit that my use-case is limited but your "trick" works. My other option in this case was to have the tocloft code revised but the maintainer seems to have given up any pretence of maintaining. I have suggested updates to several of my original packages but got no helpful responses --- not even that he would try and pass the maintenance on to someone else --- GOM. – Peter Wilson Sep 25 '21 at 18:47
3

New version of the luaotfload package (2020-12-31 luaotfload v3.16) introducess new virtual font features upper and lower. When they are used then the converting is done at font level, exactly as OP expects.

For example, in OpTeX, we can try:

\fontfam[lmfonts]

\def\UppercaseIt{\setff{upper}\currvar}

Normal text {\UppercaseIt a {\bf converted} text, accents: áéčš}, normal text.

\bye

wipet
  • 74,238
2

No solution can do exactly the same as font switching, becuase font switching is done at TeX's main processor and next processing is in "normal" mode: all commands are interpreted just now.

You can do only two pass processing, the parameter is scanned in the first pass and then it is put to \uppercase primitive. But the behavior differs, for example you can:

\fontswitch ... \dosomething exactly now in a macro .... \end{document}

but this cannot work:

\UppercaseIt ...  \dosomething exacty now in a macro ... \end{document}

The implementation of your task is only "academical". I can show you more simple implementation than scanning token per token (shown in another answers here):

\def\UppercaseIt{\afterassignment\UppercaseItA 
   \long\expandafter\def\expandafter\tmp\expandafter{\iffalse}\fi}
\def\UppercaseItA{\uppercase\expandafter{\tmp}\egroup}

Test: {\UppercaseIt some text}% Should yield SOME TEXT

\bye

wipet
  • 74,238
1

REVISED ANSWER

With the latest tokcycle package features, one does not need an explicit terminator to the token cycle. Rather, the cycle can peek ahead to see if an end-of-group/environment is next in the input stream and terminate accordingly.

Furthermore, I have created a generic command \MakeDeclarationOf which takes syntax such as

\MakeDeclarationOf\MakeUppercase\UppercaseIt
\MakeDeclarationOf\bang\Foo

to create the declaration \UppercaseIt to make subsequent things uppercase or \Foo to make subsequent things operated on by \bang.

When \MakeDeclarationOf\MakeUppercase\UppercaseIt is invoked, it makes a tokencycle environment with the name \UppercaseIt. An auxiliary command, \testendgroup is used by the tokencycle to check for end-of group/environment tokens in the input stream and, finding one, to insert the tokcycle universal terminator \endtokcycraw in the input stream.

Thus, \UppercaseIt can be terminated with \endUppercaseIt, with a }, with an \egroup, with an \endgroup, or with an \end (if at the current environment depth).

When terminated, the tokencycle takes the collected tokens of the input stream and applies a wrapper consisting of the first argument that had been supplied to \MakeDeclarationOf. Thus, the net result of having something like {\UppercaseIt abc} is that the following tokens are executed: \MakeUppercase{abc}.

The following MWE makes two such declarative commands, to demonstrate that multiple declarations don't interfere with each other.

\documentclass{article}
\usepackage{tokcycle}
\newcounter{envlevel}
\newcommand\MakeDeclarationOf[2]{
\xtokcycleenvironment#2
  {\addcytoks{####1}\testendgroup}
  {\processtoks{####1}\testendgroup}
  {\trackenvs{####1}\addcytoks{####1}\testendgroup}
  {\addcytoks{####1}\testendgroup}
  {\setcounter{envlevel}{0}}
  {\cytoks\expandafter{\expandafter#1\expandafter{\the\cytoks}}}%
}
\newcommand\trackenvs[1]{%
  \ifx\begin#1\stepcounter{envlevel}\else
   \ifx\end#1\addtocounter{envlevel}{-1}\fi\fi   
}
\newcommand\testendgroup{\tcpeek\z
  \ifx\egroup\z\tcpush{\empty\endtokcycraw}\else
  \ifx\endgroup\z\tcpush{\empty\endtokcycraw}\else
  \ifx\end\z\ifnum\value{envlevel}=0 \tcpush{\empty\endtokcycraw}\fi\fi
  \fi\fi
}

\newcommand\bang[1]{!#1!}

\begin{document} \MakeDeclarationOf\MakeUppercase\UppercaseIt \MakeDeclarationOf\bang\Foo Here is {\Foo declaration test} to see \textbackslash bang

This is a test \UppercaseIt of finding endUppercaseIt \endUppercaseIt

This is a test {\UppercaseIt of finding right brace} continuing...

This is a test \bgroup\UppercaseIt of finding egroup\egroup continuing...

This is a test \begingroup\UppercaseIt of finding endgroup \endgroup continuing...

Test \Foo of \begin{itshape}terminating at\end{itshape}

the proper \textbackslash end

\end{document}

enter image description here

  • Thank you for your effort. Unfortunately my TeXlive 2020 installation (and I can't yet update) gives an "Undefined control sequence" for \xtokcycleenvironment. – Peter Wilson Sep 25 '21 at 17:44
  • @PeterWilson You have an outdated version. Current version is v1.42 2021-08-25, Two required files to update, found at https://ctan.org/pkg/tokcycle, are tokcycle.tex and tokcycle.sty, and can be downloaded. Might as well get the PDF manuals while you are there. If you put those two files in your working directory, it should compile. – Steven B. Segletes Sep 25 '21 at 18:54