1

I am running MacTeX 2022 with all updates applied. I have a large project that last built correctly on September 3, 2022. Today I get a mysterious Missing \endcsname inserted error on one particular line and I can't find the reason. Pressing H in the console reveals that a \let control sequence is erroneously inside \csname...\endcsname but there is no \let in my code. The MWE code has been copied and pasted directly from the source project into the MWE .sty and .tex files. The only change was consistently renaming certain variables. The MWE exactly replicates the error.

This code has not been touched since September of 2022 so something in an update since September is the likely culprit. The only things I see that might be related are either use of \MakeUppercase (as I know there have been some recent modifications to that) or in the key handling mechanism. What is causing this error?

EDIT: I need the words alternate, base, and derived to be all lowercase when echoed to the console by typeout but with the first letter capitalized when used to invoke the corresponding macro. That's why I initially used \MakeUppercase which now seems to not work. I can't get \MakeTitlecase to work.

Here is the MWE package file. Name it keymwe.sty.

% !TEX program = lualatexmk
% !TEX encoding = UTF-8 Unicode

\usepackage{pgfopts} \usepackage{iftex} \RequireLuaTeX \newcommand{\keymwe@selectunits}{} \newcommand{\keymwe@selectprecision}{} \newcommand{\keymwe@selectapproximate}[2]{#1} \newcommand{\keymwe@selectprecise}[2]{#2} \newcommand{\keymwe@selectbaseunits}[3]{#1} \newcommand{\keymwe@selectderivedunits}[3]{#2} \newcommand{\keymwe@selectalternateunits}[3]{#3} \NewDocumentCommand{\AlwaysUseBaseUnits}{} {\renewcommand{\keymwe@selectunits}{\keymwe@selectbaseunits}}% \NewDocumentCommand{\AlwaysUseDerivedUnits}{} {\renewcommand{\keymwe@selectunits}{\keymwe@selectderivedunits}}% \NewDocumentCommand{\AlwaysUseAlternateUnits}{} {\renewcommand{\keymwe@selectunits}{\keymwe@selectalternateunits}}% \NewDocumentCommand{\AlwaysUseApproximateConstants}{} {\renewcommand{\keymwe@selectprecision}{\keymwe@selectapproximate}}% \NewDocumentCommand{\AlwaysUsePreciseConstants}{} {\renewcommand{\keymwe@selectprecision}{\keymwe@selectprecise}}% \NewDocumentCommand{\HereUseBaseUnits}{ m }{\begingroup\AlwaysUseBaseUnits#1\endgroup}% \NewDocumentCommand{\HereUseDerivedUnits}{ m }{\begingroup\AlwaysUseDerivedUnits#1\endgroup}% \NewDocumentCommand{\HereUseAlternateUnits}{ m }{\begingroup\AlwaysUseAlternateUnits#1\endgroup}% \NewDocumentCommand{\HereUseApproximateConstants}{ m }{\begingroup\AlwaysUseApproximateConstants#1\endgroup}% \NewDocumentCommand{\HereUsePreciseConstants}{ m }{\begingroup\AlwaysUsePreciseConstants#1\endgroup}% \NewDocumentEnvironment{UseBaseUnits}{}{\AlwaysUseBaseUnits}{}% \NewDocumentEnvironment{UseDerivedUnits}{}{\AlwaysUseDerivedUnits}{}% \NewDocumentEnvironment{UseAlternateUnits}{}{\AlwaysUseAlternateUnits}{}% \NewDocumentEnvironment{UseApproximateConstants}{}{\AlwaysUseApproximateConstants}{}% \NewDocumentEnvironment{UsePreciseConstants}{}{\AlwaysUsePreciseConstants}{}% \newif\ifusingpreciseconstants \pgfkeys{% /keymwe/options/.cd, initial@setup/.style={% /keymwe/options/buffered@units/.initial=alternate,% },% initial@setup,% preciseconstants/.is if=usingpreciseconstants,% units/.is choice,% units/.default=derived,% units/alternate/.style={/keymwe/options/buffered@units=alternate},% units/base/.style={/keymwe/options/buffered@units=base},% units/derived/.style={/keymwe/options/buffered@units=derived},% .unknown/.code={% \typeout{}% \typeout{keymwe: You used unknown option '\pgfkeyscurrentname'.}% },% }% \ProcessPgfPackageOptions{/keymwe/options}

\newcommand*{\keymwe@do@setup}{% \csname AlwaysUse\expanded{\noexpand\MakeUppercase% \pgfkeysvalueof{/keymwe/options/buffered@units}}Units\endcsname% \typeout{keymwe: You will get \pgfkeysvalueof{/keymwe/options/buffered@units}\space units.}% \ifusingpreciseconstants \AlwaysUsePreciseConstants \typeout{keymwe: You will get precise constants.}% \else \AlwaysUseApproximateConstants \typeout{keymwe: You will get approximate constants.}% \fi \typeout{}% }% \keymwe@do@setup \NewDocumentCommand{\keymwesetup}{ m }% {% \IfValueT{#1}% {% \pgfqkeys{/keymwe/options}{#1} \typeout{}% \typeout{keymwe: keymwesetup options...} \keymwe@do@setup }% }%

Here is the MWE document that uses the test package. Name it keymwe.tex.

% !TEX program = lualatexmk
% !TEX encoding = UTF-8 Unicode

\documentclass{article}

\usepackage{keymwe}

\begin{document}

Hello, world.

\end{document}

  • Why even deal with \MakeUppercase? Just use a capital A, B or D when setting the value of buffered@units. All you need then is \pgfkeysvalueof without any \MakeUppercase or expansion control. – Qrrbrbirlbel Jan 12 '23 at 00:41
  • @Qrrbrbirlbel Here's where I documented the need for \MakeUppercase. You're saying this isn't necessary? https://tex.stackexchange.com/q/654851/218142 – LaTeXereXeTaL Jan 12 '23 at 01:24
  • I don't understand what's stopping you from doing units/alternate/.style={/keymwe/options/buffered@units=Alternate} and similar. Doesn't seem like buffered@units is supposed to be used on the user-level (so no unexpected value there). You can even just simply define/\let \AlwaysUsealternateUnits to the same definitions. That said, yes, something changed with \MakeUppercase and variants. (Check texdoc source2e.) – Qrrbrbirlbel Jan 12 '23 at 01:41
  • If I am seeing this right (check also texdoc usrguide), with your small example \csname AlwaysUse\MakeTitlecase{\pgfkeysvalueof{/keymwe/options/buffered@units}Units\endcsname should the trick. If buffered@units contains more than one word but only the first letter should be capitalized: \pgfkeysgetvalue{…/buffered@units}\temp \csnameAlwaysUse\expandafter\MakeUppercase\temp Units\endcsname. But I can't test it right now. – Qrrbrbirlbel Jan 12 '23 at 01:43
  • Even if it works: using MakeUppercase in a csname looks wrong. That is a command meant for text and typesetting, not for a programming task. – Ulrike Fischer Jan 12 '23 at 07:51
  • 2
    MakeUppercase changed recently but it has never been expandable and would always have given an error in csname, the code shown can't have worked previously I think – David Carlisle Jan 12 '23 at 09:40
  • It worked perfectly. Why would I lie about that? You showed the construct to me here https://tex.stackexchange.com/a/654853/218142. The MWE you gave there no longer works either and fails with the same error. Something somewhere has changed. – LaTeXereXeTaL Jan 12 '23 at 10:26
  • It was taken from David's answer!? (although to be fair ● you originally used that in your question, David just happen to not notice the issue, and ● csname...endcsname "ignores" the \protected status, so with some particular implementation it might just "appear" to work properly) -- in either case, using \MakeUppercase in \csname-type expansion is not supported but it does happen to work before. Better to stick with explicitly-documented-to-work ("external") interfaces. – user202729 Jan 12 '23 at 10:35
  • I understand that, but I can't stick to something that I don't know is documented, where it is documented, and under what topics/issues it is documented. The documentation is spread out over many sources and not always efficiently organized. – LaTeXereXeTaL Mar 03 '24 at 03:31

1 Answers1

1

Define an expandable string case changing function. Here

\ExplSyntaxOn
\NewExpandableDocumentCommand{\Titlecasestring}{m}
 {
  \str_uppercase:f { \str_head:e { #1 } } \str_tail:e { #1 }
 }
\cs_generate_variant:Nn \str_head:n { e }
\cs_generate_variant:Nn \str_tail:n { e }
\ExplSyntaxOff

With \str_head:e you first expand the argument and then take the first item in the obtained string. Strings are used here because the construct is to be used inside \csname.

Full code.

\usepackage{pgfopts}
\usepackage{iftex}
\RequireLuaTeX
\newcommand*{\keymwe@selectunits}{}
\newcommand*{\keymwe@selectprecision}{}
\newcommand*{\keymwe@selectapproximate}[2]{#1}
\newcommand*{\keymwe@selectprecise}[2]{#2}
\newcommand*{\keymwe@selectbaseunits}[3]{#1}
\newcommand*{\keymwe@selectderivedunits}[3]{#2}
\newcommand*{\keymwe@selectalternateunits}[3]{#3}

\NewDocumentCommand{\AlwaysUseBaseUnits}{} {\renewcommand{\keymwe@selectunits}{\keymwe@selectbaseunits}} \NewDocumentCommand{\AlwaysUseDerivedUnits}{} {\renewcommand{\keymwe@selectunits}{\keymwe@selectderivedunits}} \NewDocumentCommand{\AlwaysUseAlternateUnits}{} {\renewcommand{\keymwe@selectunits}{\keymwe@selectalternateunits}} \NewDocumentCommand{\AlwaysUseApproximateConstants}{} {\renewcommand{\keymwe@selectprecision}{\keymwe@selectapproximate}} \NewDocumentCommand{\AlwaysUsePreciseConstants}{} {\renewcommand*{\keymwe@selectprecision}{\keymwe@selectprecise}} \NewDocumentCommand{\HereUseBaseUnits}{ m }{\begingroup\AlwaysUseBaseUnits#1\endgroup} \NewDocumentCommand{\HereUseDerivedUnits}{ m }{\begingroup\AlwaysUseDerivedUnits#1\endgroup} \NewDocumentCommand{\HereUseAlternateUnits}{ m }{\begingroup\AlwaysUseAlternateUnits#1\endgroup} \NewDocumentCommand{\HereUseApproximateConstants}{ m }{\begingroup\AlwaysUseApproximateConstants#1\endgroup} \NewDocumentCommand{\HereUsePreciseConstants}{ m }{\begingroup\AlwaysUsePreciseConstants#1\endgroup} \NewDocumentEnvironment{UseBaseUnits}{}{\AlwaysUseBaseUnits}{} \NewDocumentEnvironment{UseDerivedUnits}{}{\AlwaysUseDerivedUnits}{} \NewDocumentEnvironment{UseAlternateUnits}{}{\AlwaysUseAlternateUnits}{} \NewDocumentEnvironment{UseApproximateConstants}{}{\AlwaysUseApproximateConstants}{} \NewDocumentEnvironment{UsePreciseConstants}{}{\AlwaysUsePreciseConstants}{} \newif\ifusingpreciseconstants \pgfkeys{ /keymwe/options/.cd, initial@setup/.style={ /keymwe/options/buffered@units/.initial=alternate, }, initial@setup, preciseconstants/.is if=usingpreciseconstants, units/.is choice, units/.default=derived, units/alternate/.style={/keymwe/options/buffered@units=alternate}, units/base/.style={/keymwe/options/buffered@units=base}, units/derived/.style={/keymwe/options/buffered@units=derived}, .unknown/.code={% \typeout{}% \typeout{keymwe: You used unknown option '\pgfkeyscurrentname'.}% }, } \ProcessPgfPackageOptions{/keymwe/options}

\ExplSyntaxOn \NewExpandableDocumentCommand{\Titlecasestring}{m} { \str_uppercase:f { \str_head:e { #1 } } \str_tail:e { #1 } } \cs_generate_variant:Nn \str_head:n { e } \cs_generate_variant:Nn \str_tail:n { e } \ExplSyntaxOff

\newcommand*{\keymwe@do@setup}{% \csname AlwaysUse\Titlecasestring{\pgfkeysvalueof{/keymwe/options/buffered@units}}Units\endcsname \typeout{keymwe: You will get \pgfkeysvalueof{/keymwe/options/buffered@units}\space units.}% \ifusingpreciseconstants \AlwaysUsePreciseConstants \typeout{keymwe: You will get precise constants.}% \else \AlwaysUseApproximateConstants \typeout{keymwe: You will get approximate constants.}% \fi \typeout{}% }% \keymwe@do@setup \NewDocumentCommand{\keymwesetup}{ m }% {% \IfValueT{#1}% {% \pgfqkeys{/keymwe/options}{#1}% \typeout{}% \typeout{keymwe: keymwesetup options...}% \keymwe@do@setup }% }

egreg
  • 1,121,712