# Expandably change letter case and use inside \csname, without a package

I want to change the letter-case of a single character argument, and use that changed letter inside a \csname. And I want to do it without an additional package.

For example, I want to pass a macro the letter {S}, and have it execute the macro \csname macros\endcsname, or pass it the letter {T}, and have it execute the macro \csname macrot\endcsname.

In my MWE, I show three approaches:

1. I didn't expect \lowercase to work, since I recall reading that it is not truly expanded until the last possible moment. Surprisingly, it goes into an \edef okay, but it must not be truly expanded, because it breaks the \csname.

2. I also formulated an approach where I used \numexpr to add 32 to the ASCII code of the argument and perform a \char upon that result. Like \lowercase, it gives the appearance of being expandable, but it also fails inside the \csname.

3. I finally achieve success with the stringstrings package. Having written the package, I know the conversion was not pretty... essentially, it is an exhaustive 26-fold check of the form \if #1Aa\else\if #1Bb\else\if #1Cc\else...\fi\fi\fi, which has the virtue that it can be expandably placed into an \edef, and so it works.

There has to be a better way. The MWE works only because I use the inefficiently calculated \tmp from METHOD 3 inside the \csname. If I use \tmp from either of the two earlier methods, it breaks.

\documentclass{article}
\usepackage{stringstrings}
\def\macros{Expandably converted an S to a s}
\def\macrot{Expandably converted a T to a t}
\newcommand\convert[1]{%
%MAKE THE ARGUMENT LOWER CASE
1) LOWERCASE\\
\edef\tmp{\lowercase{#1}}% PRETENDS TO EXPAND, BUT FAILS IN \csname
Here is the expanded'' conversion: \tmp\\
% OR
2) ASCII CONVERSION\\
\edef\tmp{\expandafter\char\numexpr#1+32\relax}% PRETENDS TO EXPAND, BUT FAILS IN \csname
Here is the expanded'' conversion: \tmp\\
% OR
3) STRINGSTRINGS PACKAGE\\
\caselower[q]{#1}\def\tmp{\thestring}% WORKS BUT USES A PACKAGE
Here is the expanded'' conversion: \tmp\\
%
Now use it in a csname: \csname macro\tmp\endcsname%
}
\begin{document}
\convert{S}\par
\convert{T}
\end{document}


I have added egreg's code snippets into this MWE to produce a document (and not just terminal output), showing the methods put forth to this point:

\documentclass{article}

%%%%%%%%%%%%%%%%%%%%%%%%%%%% L3 APPROACH

\usepackage{expl3}

\ExplSyntaxOn
\cs_set_eq:NN \Xexplower \tl_expandable_lowercase:n
\ExplSyntaxOff

%%%%%%%%%%%%%%%%%%%%%%%%%%%% EGREG'S LESS HACKY SOLUTION

\def\explowerchar#1{%
\ifcase\numexpr#1-A\relax
a\or b\or c\or d\or e\or f\or g\or h\or i\or j\or k\or l\or m\or
n\or o\or p\or q\or r\or s\or t\or u\or v\or w\or x\or y\or z\else
#1\fi
}

\def\explower#1{%
\doexplowerchar#1\relax
}

\def\doexplowerchar#1{%
\ifx#1\relax
\else
\explowerchar{#1}\expandafter\doexplowerchar
\fi
}

%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEVE'S HACKY PACKAGE APPROACH

\usepackage{stringstrings}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\def\macros{Expandably converted an S to a s}
\def\macrot{Expandably converted a T to a t}

\begin{document}

\textbf{Steve's hacky approach}

\caselower[q]{S}\csname macro\thestring\endcsname\par
\caselower[q]{T}\csname macro\thestring\endcsname

\textbf{egreg's less hacky approach}

\edef\lcstring{\explowerchar{S}}\csname macro\lcstring\endcsname\par
\edef\lcstring{\explower{T}}\csname macro\lcstring\endcsname

\textbf{L3 approach}

\edef\lcstring{\Xexplower{S}}\csname macro\lcstring\endcsname\par
\edef\lcstring{\Xexplower{T}}\csname macro\lcstring\endcsname

\end{document}


-
My feeling for the original problem, however, is that you should simply define \macrot and \macroT, so the case is not relevant. –  egreg Apr 25 '14 at 17:16
@egreg For a question such as this, one boils the problem to the essence. The real problem at hand references this answer of mine, tex.stackexchange.com/questions/173209/…, in which capital letters are \active. Therefore I need to deal in macros with lowercase letters only in their names (note my use of stringstrings in that answer). –  Steven B. Segletes Apr 25 '14 at 17:37
Well, you have discovered why it's better not to activate letters. ;-) However, \string A gives a category code 12 A independently of the current category code of A. –  egreg Apr 25 '14 at 17:39

If you know that the input consists only of characters with category code 11 or 12 (or maybe others, but definitely not control sequences), a less horrible hack is

\def\explowerchar#1{%
\ifcase\numexpr#1-A\relax
a\or b\or c\or d\or e\or f\or g\or h\or i\or j\or k\or l\or m\or
n\or o\or p\or q\or r\or s\or t\or u\or v\or w\or x\or y\or z\else
#1\fi
}

\def\explower#1{%
\doexplowerchar#1\relax
}

\def\doexplowerchar#1{%
\ifx#1\relax
\else
\explowerchar{#1}\expandafter\doexplowerchar
\fi
}

\edef\lcstring{\explower{AStRiNg?}}
\show\lcstring


Output on the terminal:

> \lcstring=macro:
->astring?.


Needs e-TeX, of course.

There's no way of doing \lowercase expandably in full generality, I'm afraid.

Without reinventing the wheel:

\documentclass{article}
\usepackage{expl3}

\ExplSyntaxOn
\cs_set_eq:NN \explower \tl_expandable_lowercase:n
\ExplSyntaxOff

\edef\lcstring{\explower{A StRiNg wiTh SpaC\'Es?}}
\show\lcstring


Terminal output:

> \lcstring=macro:
->astring with spac\'es?.

-
No, if you use Knuth TeX. But I tested it with pdftex, pdflatex, lualatex and xelatex. Compile from the terminal or use \meaning instead of \show. –  egreg Apr 25 '14 at 15:46
Fascinating. I'm not used to operating in a "terminal condition." –  Steven B. Segletes Apr 25 '14 at 15:50
@StevenB.Segletes \show uses the same code as error messages, so it can't be used in an interactive session. I added an easier version. ;-) –  egreg Apr 25 '14 at 15:53
The first works fine in Knuth TeX with the help of package intcalc: \input intcalc.sty\relax or \usepackage{intcalc} and the following \ifcase line: \ifcase\intcalcSub{\number#1}{65} % –  Heiko Oberdiek Apr 25 '14 at 15:58
Indeed, we are currently planning to add 'case folding' to expl3 with a more 'sensible' name (currently \str_fold_case:n): full 'lowercasing' is more-or-less impossible expandably due to requirements of Unicode specs. –  Joseph Wright Apr 25 '14 at 16:00

I know you've asked for a package-free solution, but for reference the following the the approach we are likely to take to this problem in expl3. The 'design brief' here is that case folding (as defined by the Unicode people) should take place to leave 'caseless' data. Clearly this is only fully doable for LuaTeX/XeTeX: the 'fall back' for pdfTeX is to restrict the data used to only ASCII letters. Note that the code here is from Bruno: I tried a simpler approach but the following is more elegant and much faster!

The 'business end' of the approach is a set up (using expl3 syntax):

\cs_new:Npn \str_fold_case:n #1
{
\exp_after:wN \__str_fold_auxi:w \tl_to_str:n {#1}
{ ~ \c_empty_tl } \__str_fold_end:w ? ~
}
\cs_new:Npn \__str_fold_auxi:w #1 ~
{
\__str_fold_auxii:N #1 { ~ \c_space_tl }
\__str_fold_auxi:w
}
\cs_new:Npn \__str_fold_auxii:N #1
{
\exp_after:wN \__str_fold_auxiii:NNNNNNNN
\int_use:N \__int_eval:w 1000000 + #1 \__int_eval_end: #1
}
\cs_new:Npn \__str_fold_auxiii:NNNNNNNN #1#2#3#4#5#6#7#8
{
\exp_args:NNv \str_case_x:nnF #8
{ c__str_case_#6_X_#7_tl }
{ #8 \exp_after:wN \use_none:n #8 }
\__str_fold_auxii:N
}
\cs_new:Npn \__str_fold_end:w ? #1 \__str_fold_auxi:w { }


which is therefore ultimately dependent on \pdfstrcmp (hidden inside \str_case_x:nnF) to do a string comparison. The above also needs data: this can be auto-generated from the Unicode case-folding file and currently comes out as:

\tl_const:cn {c__str_case_0_X_0_tl} {ÈèĬĭƐɛǴǵϨϩҰұԔԕḔḕṸṹỜờᾤ{ὤι}ⒸⓒⰤⱔⲈⲉꙨꙩ}
\tl_const:cn {c__str_case_0_X_1_tl} {ÉéƑƒჍⴭᾥ{ὥι}ⒹⓓⰥⱕⳭⳮ}
\tl_const:cn {c__str_case_0_X_2_tl} {ÊêĮįǶƕΆάϪϫҲҳԖԗḖḗṺṻỞởᾦ{ὦι}ⒺⓔⰦⱖⲊⲋꙪꙫꜲꜳ}
\tl_const:cn {c__str_case_0_X_3_tl} {ËëƓɠǷƿᾧ{ὧι}ⒻⓕⰧⱗ}
\tl_const:cn {c__str_case_0_X_4_tl} {Ììİ{i̇}ƔɣǸǹΈέϬϭҴҵԘԙḘḙṼṽỠỡᾨ{ὠι}ⒼⓖⰨⱘⲌⲍꙬꙭꜴꜵ}
\tl_const:cn {c__str_case_0_X_5_tl} {ÍíΉήᾩ{ὡι}ⒽⓗⰩⱙ}
\tl_const:cn {c__str_case_0_X_6_tl} {ÎîĲĳƖɩǺǻΊίϮϯҶҷԚԛḚḛṾṿỢợᾪ{ὢι}ⒾⓘⰪⱚⲎⲏⳲⳳꜶꜷ}
\tl_const:cn {c__str_case_0_X_7_tl} {ÏïƗɨᾫ{ὣι}ⒿⓙⰫⱛ}
\tl_const:cn {c__str_case_0_X_8_tl} {ÐðĴĵƘƙǼǽΌόϰκҸҹԜԝḜḝẀẁỤụὈὀᾬ{ὤι}ⓀⓚⰬⱜⲐⲑꜸꜹ}
\tl_const:cn {c__str_case_0_X_9_tl} {ÑñϱρὉὁᾭ{ὥι}ⓁⓛⰭⱝ}
\tl_const:cn {c__str_case_1_X_0_tl} {ÒòĶķǾǿΎύҺһԞԟḞḟẂẃỦủὊὂᾮ{ὦι}ⓂⓜⰮⱞⲒⲓꜺꜻ}
\tl_const:cn {c__str_case_1_X_1_tl} {ÓóΏώὋὃᾯ{ὧι}Ⓝⓝ}
\tl_const:cn {c__str_case_1_X_2_tl} {ÔôƜɯȀȁΐ{ΐ}ϴθҼҽԠԡḠḡẄẅỨứὌὄⓄⓞⲔⲕꜼꜽꞠꞡ}
\tl_const:cn {c__str_case_1_X_3_tl} {ÕõĹĺƝɲΑαϵεὍὅⓅⓟＡａ}
\tl_const:cn {c__str_case_1_X_4_tl} {ÖöȂȃΒβҾҿԢԣḢḣẆẇỪừᾲ{ὰι}ⓆⓠⲖⲗꜾꜿꞢꞣＢｂ}
\tl_const:cn {c__str_case_1_X_5_tl} {ĻļƟɵΓγϷϸև{եւ}ᾳ{αι}ⓇⓡＣｃ}
\tl_const:cn {c__str_case_1_X_6_tl} {ØøƠơȄȅΔδӀӏԤԥḤḥẈẉỬửὐ{ὐ}ᾴ{άι}ⓈⓢⲘⲙꝀꝁꞤꞥＤｄ}
\tl_const:cn {c__str_case_1_X_7_tl} {ÙùĽľΕεϹϲӁӂⓉⓣＥｅ}
\tl_const:cn {c__str_case_1_X_8_tl} {ÚúƢƣȆȇΖζϺϻԦԧḦḧẊẋỮữὒ{ὒ}ᾶ{ᾶ}ⓊⓤⲚⲛꝂꝃꞦꞧＦｆ}
\tl_const:cn {c__str_case_1_X_9_tl} {ÛûĿŀΗηӃӄᾷ{ᾶι}ⓋⓥＧｇ}
\tl_const:cn {c__str_case_2_X_0_tl} {ÜüƤƥȈȉΘθѠѡḨḩẌẍỰựὔ{ὔ}ᾸᾰⓌⓦⲜⲝꝄꝅꞨꞩＨｈ}
\tl_const:cn {c__str_case_2_X_1_tl} {ÝýŁłΙιϽͻӅӆᾹᾱⓍⓧＩｉ}
\tl_const:cn {c__str_case_2_X_2_tl} {ÞþƦʀȊȋΚκϾͼѢѣḪḫẎẏỲỳὖ{ὖ}ᾺὰⓎⓨⲞⲟꝆꝇꞪɦＪｊ}
\tl_const:cn {c__str_case_2_X_3_tl} {ß{ss}ŃńƧƨΛλϿͽӇӈΆάⓏⓩＫｋ}
\tl_const:cn {c__str_case_2_X_4_tl} {ȌȍΜμЀѐѤѥḬḭẐẑỴỵᾼ{αι}ⲠⲡꚀꚁꝈꝉＬｌ}
\tl_const:cn {c__str_case_2_X_5_tl} {ŅņƩʃΝνЁёӉӊὙὑＭｍ}
\tl_const:cn {c__str_case_2_X_6_tl} {ȎȏΞξЂђѦѧḮḯẒẓỶỷιιⲢⲣꚂꚃꝊꝋＮｎ}
\tl_const:cn {c__str_case_2_X_7_tl} {ŇňΟοЃѓӋӌὛὓＯｏ}
\tl_const:cn {c__str_case_2_X_8_tl} {ƬƭȐȑΠπЄєѨѩḰḱẔẕỸỹⲤⲥꚄꚅꝌꝍＰｐ}
\tl_const:cn {c__str_case_2_X_9_tl} {ŉ{ʼn}ΡρЅѕӍӎԱաὝὕＱｑ}
\tl_const:cn {c__str_case_3_X_0_tl} {ŊŋƮʈȒȓІіѪѫԲբḲḳẖ{ẖ}Ỻỻῂ{ὴι}ⲦⲧꚆꚇꝎꝏＲｒ}
\tl_const:cn {c__str_case_3_X_1_tl} {ƯưΣσЇїԳգẗ{ẗ}Ὗὗῃ{ηι}Ｓｓ}
\tl_const:cn {c__str_case_3_X_2_tl} {ŌōȔȕΤτЈјѬѭӐӑԴդḴḵẘ{ẘ}Ỽỽῄ{ήι}ⲨⲩꚈꚉꝐꝑＴｔ}
\tl_const:cn {c__str_case_3_X_3_tl} {ƱʊΥυЉљԵեẙ{ẙ}Ｕｕ}
\tl_const:cn {c__str_case_3_X_4_tl} {ŎŏƲʋȖȗΦφЊњѮѯӒӓԶզḶḷẚ{aʾ}Ỿỿῆ{ῆ}ⲪⲫꚊꚋꝒꝓＶｖ}
\tl_const:cn {c__str_case_3_X_5_tl} {ƳƴΧχЋћԷէẛṡῇ{ῆι}Ｗｗ}
\tl_const:cn {c__str_case_3_X_6_tl} {ŐőȘșΨψЌќѰѱӔӕԸըḸḹῈὲⲬⲭꚌꚍꝔꝕＸｘ}
\tl_const:cn {c__str_case_3_X_7_tl} {ƵƶͅιΩωЍѝԹթΈέＹｙ}
\tl_const:cn {c__str_case_3_X_8_tl} {ŒœȚțΪϊЎўѲѳӖӗԺժḺḻẞ{ss}ῊὴⲮⲯꚎꚏꝖꝗＺｚ}
\tl_const:cn {c__str_case_3_X_9_tl} {ƷʒΫϋЏџԻիΉή}
\tl_const:cn {c__str_case_4_X_0_tl} {ŔŕƸƹȜȝАаѴѵӘәԼլḼḽẠạὨὠῌ{ηι}ⲰⲱꚐꚑꝘꝙ}
\tl_const:cn {c__str_case_4_X_1_tl} {БбԽխὩὡ}
\tl_const:cn {c__str_case_4_X_2_tl} {ŖŗȞȟВвѶѷӚӛԾծḾḿẢảὪὢⲲⲳꚒꚓꝚꝛ}
\tl_const:cn {c__str_case_4_X_3_tl} {ГгԿկὫὣ}
\tl_const:cn {c__str_case_4_X_4_tl} {ŘřƼƽȠƞΰ{ΰ}ДдѸѹӜӝՀհṀṁẤấἈἀὬὤⅠⅰⲴⲵꚔꚕꝜꝝ}
\tl_const:cn {c__str_case_4_X_5_tl} {ЕеՁձἉἁὭὥⅡⅱ}
\tl_const:cn {c__str_case_4_X_6_tl} {ŚśȢȣЖжѺѻӞӟՂղṂṃẦầἊἂὮὦῒ{ῒ}ⅢⅲⲶⲷꚖꚗꝞꝟ}
\tl_const:cn {c__str_case_4_X_7_tl} {ЗзՃճἋἃὯὧΐ{ΐ}Ⅳⅳ}
\tl_const:cn {c__str_case_4_X_8_tl} {ŜŝȤȥИиѼѽӠӡՄմṄṅẨẩἌἄⅤⅴⲸⲹꝠꝡ}
\tl_const:cn {c__str_case_4_X_9_tl} {ЙйՅյἍἅⅥⅵ}
\tl_const:cn {c__str_case_5_X_0_tl} {ŞşȦȧКкѾѿӢӣՆնṆṇẪẫἎἆῖ{ῖ}ⅦⅶⲺⲻꝢꝣ}
\tl_const:cn {c__str_case_5_X_1_tl} {ЛлՇշἏἇῗ{ῗ}Ⅷⅷ}
\tl_const:cn {c__str_case_5_X_2_tl} {ŠšǄǆȨȩМмҀҁӤӥՈոṈṉẬậῘῐⅨⅸⲼⲽꝤꝥ}
\tl_const:cn {c__str_case_5_X_3_tl} {ǅǆНнՉչῙῑⅩⅹ}
\tl_const:cn {c__str_case_5_X_4_tl} {ŢţȪȫОоӦӧՊպṊṋẮắῚὶⅪⅺⲾⲿꝦꝧ}
\tl_const:cn {c__str_case_5_X_5_tl} {ǇǉПпՋջΊίⅫⅻ}
\tl_const:cn {c__str_case_5_X_6_tl} {ĀāŤťǈǉȬȭРрӨөՌռႠⴀṌṍẰằⅬⅼⳀⳁꝨꝩﬀ{ff}}
\tl_const:cn {c__str_case_5_X_7_tl} {СсՍսႡⴁⅭⅽﬁ{fi}}
\tl_const:cn {c__str_case_5_X_8_tl} {ĂăŦŧǊǌȮȯТтӪӫՎվႢⴂṎṏẲẳⅮⅾⳂⳃꝪꝫﬂ{fl}}
\tl_const:cn {c__str_case_5_X_9_tl} {ǋǌУуՏտႣⴃⅯⅿﬃ{ffi}}
\tl_const:cn {c__str_case_6_X_0_tl} {ĄąŨũȰȱФфӬӭՐրႤⴄṐṑẴẵἘἐⱠⱡⳄⳅꙀꙁꝬꝭﬄ{ffl}𐐀𐐨}
\tl_const:cn {c__str_case_6_X_1_tl} {ǍǎХхՑցႥⴅἙἑﬅ{st}𐐁𐐩}
\tl_const:cn {c__str_case_6_X_2_tl} {ĆćŪūȲȳςσЦцҊҋӮӯՒւႦⴆṒṓẶặἚἒῢ{ῢ}ⱢɫⳆⳇꙂꙃꝮꝯﬆ{st}𐐂𐐪}
\tl_const:cn {c__str_case_6_X_3_tl} {ǏǐЧчՓփႧⴇἛἓΰ{ΰ}Ᵽᵽ𐐃𐐫}
\tl_const:cn {c__str_case_6_X_4_tl} {ĈĉŬŭШшҌҍӰӱՔքႨⴈṔṕẸẹἜἔᾀ{ἀι}ῤ{ῤ}ⰀⰰⱤɽⳈⳉꙄꙅ𐐄𐐬}
\tl_const:cn {c__str_case_6_X_5_tl} {AaǑǒЩщՕօႩⴉἝἕᾁ{ἁι}Ⰱⰱ𐐅𐐭}
\tl_const:cn {c__str_case_6_X_6_tl} {BbĊċŮůЪъҎҏӲӳՖֆႪⴊṖṗẺẻᾂ{ἂι}ῦ{ῦ}ⰂⰲⳊⳋꙆꙇ𐐆𐐮}
\tl_const:cn {c__str_case_6_X_7_tl} {CcǓǔЫыႫⴋᾃ{ἃι}ῧ{ῧ}ⰃⰳⱧⱨ𐐇𐐯}
\tl_const:cn {c__str_case_6_X_8_tl} {DdČčŰűЬьҐґӴӵႬⴌṘṙẼẽᾄ{ἄι}ῨῠⰄⰴⳌⳍꙈꙉ𐐈𐐰}
\tl_const:cn {c__str_case_6_X_9_tl} {EeǕǖЭэႭⴍᾅ{ἅι}ῩῡⰅⰵⱩⱪ𐐉𐐱}
\tl_const:cn {c__str_case_7_X_0_tl} {FfĎďŲųȺⱥЮюҒғӶӷႮⴎṚṛẾếᾆ{ἆι}ῪὺⰆⰶⳎⳏꙊꙋ𐐊𐐲}
\tl_const:cn {c__str_case_7_X_1_tl} {GgǗǘȻȼЯяႯⴏᾇ{ἇι}ΎύⰇⰷⱫⱬ𐐋𐐳}
\tl_const:cn {c__str_case_7_X_2_tl} {HhĐđŴŵҔҕӸӹႰⴐṜṝỀềᾈ{ἀι}ῬῥⰈⰸⳐⳑꙌꙍ𐐌𐐴}
\tl_const:cn {c__str_case_7_X_3_tl} {IiǙǚȽƚႱⴑᾉ{ἁι}ⰉⰹⱭɑꝹꝺ𐐍𐐵}
\tl_const:cn {c__str_case_7_X_4_tl} {JjĒēŶŷȾⱦҖҗӺӻႲⴒṞṟỂểᾊ{ἂι}ⰊⰺⱮɱⳒⳓꙎꙏ𐐎𐐶}
\tl_const:cn {c__str_case_7_X_5_tl} {KkǛǜϏϗႳⴓᾋ{ἃι}ⰋⰻⱯɐꝻꝼﬓ{մն}𐐏𐐷}
\tl_const:cn {c__str_case_7_X_6_tl} {LlĔĕŸÿϐβҘҙӼӽႴⴔṠṡỄễἨἠᾌ{ἄι}ⰌⰼⱰɒⳔⳕꙐꙑﬔ{մե}𐐐𐐸}
\tl_const:cn {c__str_case_7_X_7_tl} {MmŹźɁɂϑθႵⴕἩἡᾍ{ἅι}ⰍⰽꝽᵹﬕ{մի}𐐑𐐹}
\tl_const:cn {c__str_case_7_X_8_tl} {NnĖėǞǟҚқӾӿႶⴖṢṣỆệἪἢᾎ{ἆι}ῲ{ὼι}ⰎⰾⱲⱳⳖⳗꙒꙓꝾꝿﬖ{վն}𐐒𐐺}
\tl_const:cn {c__str_case_7_X_9_tl} {OoŻżɃƀႷⴗἫἣᾏ{ἇι}ῳ{ωι}ↃↄⰏⰿﬗ{մխ}𐐓𐐻}
\tl_const:cn {c__str_case_8_X_0_tl} {PpĘęǠǡɄʉͰͱҜҝԀԁႸⴘḀḁṤṥỈỉἬἤᾐ{ἠι}ῴ{ώι}ⰐⱀⳘⳙꙔꙕꞀꞁ𐐔𐐼}
\tl_const:cn {c__str_case_8_X_1_tl} {QqµμŽžɅʌϕφႹⴙἭἥᾑ{ἡι}ⰑⱁⱵⱶ𐐕𐐽}
\tl_const:cn {c__str_case_8_X_2_tl} {RrĚěǢǣɆɇͲͳϖπҞҟԂԃႺⴚḂḃṦṧỊịἮἦᾒ{ἢι}ῶ{ῶ}ⰒⱂⳚⳛꙖꙗꞂꞃ𐐖𐐾}
\tl_const:cn {c__str_case_8_X_3_tl} {SsſsႻⴛἯἧᾓ{ἣι}ῷ{ῶι}Ⱃⱃ𐐗𐐿}
\tl_const:cn {c__str_case_8_X_4_tl} {TtĜĝǤǥɈɉϘϙҠҡԄԅႼⴜḄḅṨṩỌọᾔ{ἤι}ῸὸⰔⱄⳜⳝꙘꙙꞄꞅ𐐘𐑀}
\tl_const:cn {c__str_case_8_X_5_tl} {UuƁɓႽⴝᾕ{ἥι}ΌόⰕⱅ𐐙𐑁}
\tl_const:cn {c__str_case_8_X_6_tl} {VvĞğƂƃǦǧɊɋͶͷϚϛҢңԆԇႾⴞḆḇṪṫỎỏᾖ{ἦι}ῺὼΩωⰖⱆⳞⳟꙚꙛꜢꜣꞆꞇ𐐚𐑂}
\tl_const:cn {c__str_case_8_X_7_tl} {WwႿⴟᾗ{ἧι}ΏώⰗⱇ𐐛𐑃}
\tl_const:cn {c__str_case_8_X_8_tl} {XxĠġƄƅǨǩɌɍϜϝҤҥԈԉჀⴠḈḉṬṭỐốᾘ{ἠι}ῼ{ωι}ⰘⱈⳠⳡꙜꙝꜤꜥ𐐜𐑄}
\tl_const:cn {c__str_case_8_X_9_tl} {YyჁⴡᾙ{ἡι}Ⱉⱉ𐐝𐑅}
\tl_const:cn {c__str_case_9_X_0_tl} {ZzĢģƆɔǪǫɎɏϞϟҦҧԊԋჂⴢḊḋṮṯỒồᾚ{ἢι}KkⰚⱊⱾȿⳢⳣꙞꙟꜦꜧ𐐞𐑆}
\tl_const:cn {c__str_case_9_X_1_tl} {ƇƈჃⴣᾛ{ἣι}ÅåⰛⱋⱿɀꞋꞌ𐐟𐑇}
\tl_const:cn {c__str_case_9_X_2_tl} {ÀàĤĥǬǭϠϡҨҩԌԍჄⴤḌḍṰṱỔổἸἰᾜ{ἤι}ⰜⱌⲀⲁꙠꙡꜨꜩ𐐠𐑈}
\tl_const:cn {c__str_case_9_X_3_tl} {ÁáƉɖჅⴥἹἱᾝ{ἥι}ⰝⱍꞍɥ𐐡𐑉}
\tl_const:cn {c__str_case_9_X_4_tl} {ÂâĦħƊɗǮǯϢϣҪҫԎԏḎḏṲṳỖỗἺἲᾞ{ἦι}ⰞⱎⲂⲃꙢꙣꜪꜫ𐐢𐑊}
\tl_const:cn {c__str_case_9_X_5_tl} {ÃãƋƌჇⴧἻἳᾟ{ἧι}Ⱏⱏ𐐣𐑋}
\tl_const:cn {c__str_case_9_X_6_tl} {ÄäĨĩǰ{ǰ}ϤϥҬҭԐԑḐḑṴṵỘộἼἴᾠ{ὠι}ⰠⱐⲄⲅꙤꙥꜬꜭꞐꞑ𐐤𐑌}
\tl_const:cn {c__str_case_9_X_7_tl} {ÅåǱǳἽἵᾡ{ὡι}Ⱑⱑ𐐥𐑍}
\tl_const:cn {c__str_case_9_X_8_tl} {ÆæĪīƎǝǲǳϦϧҮүԒԓḒḓṶṷỚớἾἶᾢ{ὢι}ℲⅎⒶⓐⰢⱒⲆⲇꙦꙧꜮꜯꞒꞓ𐐦𐑎}
\tl_const:cn {c__str_case_9_X_9_tl} {ÇçƏəἿἷᾣ{ὣι}ⒷⓑⰣⱓⳫⳬ𐐧𐑏}


The 'trick' here is to divide up the rather long list of chars into blocks, which means that the string comparison (relatively slow) doesn't have to map over the entire list (over 1000 chars) to find a match. As you'll see, it's only when there is a change available that there is any data to store at all.

The above should get added to expl3 'real soon now', depending on my time availability.

-
Note: probably there is some work to do on category codes: everything should come out as 'other' chars. –  Joseph Wright Apr 25 '14 at 16:14
I didn't realize I was opening a can of worms :^) –  Steven B. Segletes Apr 25 '14 at 16:16
@StevenB.Segletes The team have been working on this very area recently: case changing is a current weakness in expl3 as we've only really provided the primitives renamed. The Unicode people have worked very hard on the entire area, and it's now a question of implementing what they say. Case folding is the easiest part as there is no language/context-sensitivity. –  Joseph Wright Apr 25 '14 at 16:22
BTW, if you are really interested I'll 'convert' the expl3 to 'normal' TeX, but it's a bit of a pain here as it needs not only the 'code itself' but also the \str_case_x:nnF stuff, so will get a bit long! –  Joseph Wright Apr 25 '14 at 16:29
To paraphrase Otto von Bismarck, "LaTeX3 coding is like sausages, it is better not to see them being made." –  Steven B. Segletes Apr 25 '14 at 16:41

Since I was playing around with it 9 months after I asked the question, and developed a different approach, I will post it here. It achieves the result by making upper- or lower-cased characters active and then redefining them in the other lettercase.

It achieves the goal of the question, which was "I want to pass a macro the letter {S}, and have it execute the macro \csname macros\endcsname, or pass it the letter {T}, and have it execute the macro \csname macrot\endcsname."

However, it still has limitations, two big ones that come to mind:

1. I can't operate operate on \def'ed information. Thus, I can say \macrofylc{T} and have it execute \csname macrot\endcsname, but I can't say \def\x{T}\macrofylc{\x}.

2. It can handle macros in its arguments, but only if the macros are named in letters that are not being made active. Thus, I can \expresslc{This is An \textit{ITALIC} test} to get "this is an italic test", but I to make it upper case, I need the following kludge: \let\TEXTIT\textit\expressuc{This is An \TEXTIT{ITALIC} test} to get "THIS IS AN ITALIC TEST"

Note that limitation #2 is not a whole lot worse than the current state of affairs in LaTeX2e, since we are talking about things that are not regular expressions. However, I would dearly like to remedy limitation #1, but don't see how at this point.

Here is the MWE:

\documentclass{article}
\def\setuc{%
\8101\9A=\8102\9\8101\9B=\8102\9\8101\9C=\8102\9\8101\9D=\8102\9%
\8101\9E=\8102\9\8101\9F=\8102\9\8101\9G=\8102\9\8101\9H=\8102\9%
\8101\9I=\8102\9\8101\9J=\8102\9\8101\9K=\8102\9\8101\9L=\8102\9%
\8101\9M=\8102\9\8101\9N=\8102\9\8101\9O=\8102\9\8101\9P=\8102\9%
\8101\9Q=\8102\9\8101\9R=\8102\9\8101\9S=\8102\9\8101\9T=\8102\9%
\8101\9U=\8102\9\8101\9V=\8102\9\8101\9W=\8102\9\8101\9X=\8102\9%
\8101\9Y=\8102\9\8101\9Z=\8102\9%
}
\def\setlc{%
\8101\9a=\8102\9\8101\9b=\8102\9\8101\9c=\8102\9\8101\9d=\8102\9%
\8101\9e=\8102\9\8101\9f=\8102\9\8101\9g=\8102\9\8101\9h=\8102\9%
\8101\9i=\8102\9\8101\9j=\8102\9\8101\9k=\8102\9\8101\9l=\8102\9%
\8101\9m=\8102\9\8101\9n=\8102\9\8101\9o=\8102\9\8101\9p=\8102\9%
\8101\9q=\8102\9\8101\9r=\8102\9\8101\9s=\8102\9\8101\9t=\8102\9%
\8101\9u=\8102\9\8101\9v=\8102\9\8101\9w=\8102\9\8101\9x=\8102\9%
\8101\9y=\8102\9\8101\9z=\8102\9%
}
\def\resetuc{%
\8101\9A=11\8101\9B=11\8101\9C=11\8101\9D=11%
\8101\9E=11\8101\9F=11\8101\9G=11\8101\9H=11%
\8101\9I=11\8101\9J=11\8101\9K=11\8101\9L=11%
\8101\9M=11\8101\9N=11\8101\9O=11\8101\9P=11%
\8101\9Q=11\8101\9R=11\8101\9S=11\8101\9T=11%
\8101\9U=11\8101\9V=11\8101\9W=11\8101\9X=11%
\8101\9Y=11\8101\9Z=11%
}
\def\resetlc{%
\8101\9a=11\8101\9b=11\8101\9c=11\8101\9d=11%
\8101\9e=11\8101\9f=11\8101\9g=11\8101\9h=11%
\8101\9i=11\8101\9j=11\8101\9k=11\8101\9l=11%
\8101\9m=11\8101\9n=11\8101\9o=11\8101\9p=11%
\8101\9q=11\8101\9r=11\8101\9s=11\8101\9t=11%
\8101\9u=11\8101\9v=11\8101\9w=11\8101\9x=11%
\8101\9y=11\8101\9z=11%
}
\let\8\csname
\let\9\endcsname
\expandafter\let\csname101\endcsname\catcode
\expandafter\let\csname102\endcsname\active
\expandafter\let\csname103\endcsname\gdef
\expandafter\let\csname104\endcsname\resetuc
\expandafter\let\csname105\endcsname\resetlc
%%%%%%%%%%%%%%%%%%%%%%%%%
\def\macrofylc{\setuc\macrotransform}
\def\macrofyuc{\setlc\macrotransform}
\def\expresslc{\setuc\transform}
\def\expressuc{\setlc\transform}
\def\deflc{\setuc\deftransform}
\def\defuc{\setlc\deftransform}
%%%%%%%%%%%%%%%%%%%%%%%%%
\def\macrotransform#1{\csname macro#1\endcsname\resetuc\resetlc}
\def\transform#1{#1\resetuc\resetlc}
\def\deftransform#1{\def\thestring{#1}\resetuc\resetlc}
%%%%%%%%%%%%%%%%%%%%%%%%%
\setuc
% REDEFINE uc TO LOWERCASE
\8103\9A{a}\8103\9B{b}\8103\9C{c}\8103\9D{d}\8103\9E{e}\8103\9F{f}%
\8103\9G{g}\8103\9H{h}\8103\9I{i}\8103\9J{j}\8103\9K{k}\8103\9L{l}%
\8103\9M{m}\8103\9N{n}\8103\9O{o}\8103\9P{p}\8103\9Q{q}\8103\9R{r}%
\8103\9S{s}\8103\9T{t}\8103\9U{u}\8103\9V{v}\8103\9W{w}\8103\9X{x}%
\8103\9Y{y}\8103\9Z{z}%
\8104\9% \resetuc
%%%%%%%%%%%%%%%%%%%%%%%%%
\setlc
% REDEFINE LOWERCASE TO uc
\8103\9a{A}\8103\9b{B}\8103\9c{C}\8103\9d{D}\8103\9e{E}\8103\9f{F}%
\8103\9g{G}\8103\9h{H}\8103\9i{I}\8103\9j{J}\8103\9k{K}\8103\9l{L}%
\8103\9m{M}\8103\9n{N}\8103\9o{O}\8103\9p{P}\8103\9q{Q}\8103\9r{R}%
\8103\9s{S}\8103\9t{T}\8103\9u{U}\8103\9v{V}\8103\9w{W}\8103\9x{X}%
\8103\9y{Y}\8103\9z{Z}%
\8105\9% \resetlc
%%%%%%%%%%%%%%%%%%%%%%%%%
% THESE WILL TELL ME IF I HAVE SUCCEEDED OR NOT
\def\macrot{You have executed macrot}
\def\macroT{You have executed macroT}
\def\macrost{You have executed macrost}
\def\macroST{You have executed macroST}
\begin{document}
\textbf{Express directly: lc, then uc}\par
\expresslc{This IS a \textbf{Bold} \textit{Italic} test}\par
\let\TEXTBF\textbf\let\TEXTIT\textit% THIS IS A CHEAT
\expressuc{This IS a \TEXTBF{Bold} \TEXTIT{Italic} test that cheats.}\par

\textbf{Macrofy lc:}\par
\macrofylc{T}\par
\macrofylc{t}\par
\macrofylc{ST}\par
\macrofylc{St}\par
\macrofylc{sT}\par
\textbf{Macrofy uc:}\par
\macrofyuc{T}\par
\macrofyuc{t}\par
\macrofyuc{st}\par
\macrofyuc{sT}\par
\macrofyuc{St}\par

\textbf{Place in def:}\par
\deflc{This IS a \textbf{Bold} CASE}
Here is thestring stored by deflc: \thestring\par
\textit{UPPERCASE and lowercase are restored.}\par
\end{document}


-
f expansion (in expl3 terms) is expandable itself, you could apply it to the argument before passing the argument to \macrofylc{\x}. I don't know how exactly is it implemented, but it's the common \romannumeral\Q.. (or something like that, I don't really know). –  Manuel Jan 16 at 20:50
@Manuel Thanks for the reminder about the \rommannumeral trick. I also don't really understand it, but even a monkey, given enough random tries at the typewriter, could produce Shakespeare's sonnets. Maybe I'll try to be that monkey. –  Steven B. Segletes Jan 16 at 21:02
Search in expl3-code.tex for \exp_args:Nf and find what does it do (that's exactly what you need, if I understood correctly, so \exp_args:Nf\macrofylc{\x} should work). In any case, I think there are lots of examples in this site about the \romannumeral trick, you don't need that monkey, just a bit of search and trial and error. –  Manuel Jan 16 at 21:08
Isn't it just \expandafter\macrofylc\expandafter{\romannumeral\Q\x}? (I mean that's the idea, you need to include that in the macro, but that's easy.) –  Manuel Jan 16 at 21:48
@Manuel The problem seems to be one of catcodes, not expansion. When a \def\x{T} occurs in advance of an invocation to my macro, the "T" in \x is catcode 12. Thus, when passed to my macro, the making of "T" to an active character does not retroactively change the catcode of "T" belonging to \x`. –  Steven B. Segletes Jan 16 at 22:00