summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/l3kernel/l3candidates.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3candidates.dtx')
-rw-r--r--Master/texmf-dist/source/latex/l3kernel/l3candidates.dtx904
1 files changed, 821 insertions, 83 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3candidates.dtx b/Master/texmf-dist/source/latex/l3kernel/l3candidates.dtx
index 440ceb898bb..5e6956729dc 100644
--- a/Master/texmf-dist/source/latex/l3kernel/l3candidates.dtx
+++ b/Master/texmf-dist/source/latex/l3kernel/l3candidates.dtx
@@ -38,7 +38,7 @@
\documentclass[full]{l3doc}
%</driver>
%<*driver|package>
-\GetIdInfo$Id: l3candidates.dtx 5499 2015-01-27 14:33:48Z joseph $
+\GetIdInfo$Id: l3candidates.dtx 5523 2015-02-12 22:10:34Z joseph $
{L3 Experimental additions to l3kernel}
%</driver|package>
%<*driver>
@@ -806,7 +806,7 @@
% \end{texnote}
% \end{function}
%
-% \begin{function}[rEXP, added = 2014-06-30]^^A
+% \begin{function}[rEXP, added = 2014-06-30, updated = 2015-02-12]^^A
% {
% \tl_lower_case:n, \tl_upper_case:n, \tl_mixed_case:n,
% \tl_lower_case:nn, \tl_upper_case:nn, \tl_mixed_case:nn
@@ -819,42 +819,103 @@
% regarded broadly as \enquote{text}. They traverse the \meta{tokens} and
% change the case of characters as discussed below. The character code of
% the characters replaced may be arbitrary: the replacement characters will
-% have stand document-level category codes ($11$ for letters, $12$ for
+% have standard document-level category codes ($11$ for letters, $12$ for
% letter-like characters which can also be case-changed).
% \end{function}
%
-% The functions are \texttt{x}-type expandable: tokens are returned protected
-% from further expansion where appropriate. Begin-group and end-group
-% characters in the \meta{tokens} are normalized and become |{| and |}|,
-% respectively. Any tokens within such a group will \emph{not} be
-% case-changed, and thus for example
+% The functions are \texttt{x}-type expandable and will expand input in an
+% analogous way (though they are themselves expandable). In particular, input
+% in the form of token lists or expandable functions will be expanded
+% \emph{unless} it falls within one of the special handling classes described
+% below. As such, in package mode \LaTeXe{} \enquote{robust} commands should
+% be converted to engine-protected versions using for example the
+% \tn{robustify} command from the \pkg{etoolbox} package. Begin-group and
+% end-group characters in the \meta{tokens} are normalized and become |{| and
+% |}|, respectively. Case changing will take place inside groups but not in
+% math mode material so for example
+% \begin{verbatim}
+% \tl_upper_case:n { Some~text~$y = mx + c$~with~{Braces} }
+% \end{verbatim}
+% will become
+% \begin{verbatim}
+% SOME~TEXT~$y = mx + c$~WITH~{BRACES}
+% \end{verbatim}
+%
+% \begin{variable}{\l_tl_case_change_exclude_tl}
+% Case changing can be prevented by using any command on the list
+% \cs{l_tl_case_change_exclude_tl}. Each entry should be a function
+% to be followed by one argument: the latter will be preserved as-is.
+% Thus for example following
% \begin{verbatim}
-% \tl_upper_case:n { Some~text~{$y = mx + c$}~with~{Protection} }
+% \tl_put_right:Nn \l_tl_case_change_exclude_tl { \NoChangeCase }
% \end{verbatim}
-% will become
+% the input
% \begin{verbatim}
-% SOME~TEXT~{$y = mx + c$}~WITH~{Protection}
+% \tl_upper_case:n
+% { Some~text~$y = mx + c$~with~\NoChangeCase {Protection} }
% \end{verbatim}
+% will result in
+% \begin{verbatim}
+% SOME~TEXT~$y = mx + c$~WITH~\NoChangeCase {Protection}
+% \end{verbatim}
+% Notice that the case changing mapping preserves the inclusion of
+% the escape functions: it is left to other code to provide suitable
+% definitions (typically equivalent to \cs{use:n}). In particular, these
+% functions are protected from further expansion (returned using
+% \cs{exp_not:n}) as so will not be lost inside an \texttt{x}-type expansion.
+%
+% When used with \LaTeXe{} the commands |\cite|, |\ensuremath|, |\label|
+% and |\ref| are automatically included in the list for exclusion from
+% case changing.
+% \end{variable}
+%
+% \begin{variable}{\l_tl_case_change_math_tl}
+% Case changing is automatically skipped in math mode, as shown in the
+% demonstrations above. This is controlled by the list of tokens in
+% \cs{l_tl_case_change_math_tl}, which should be in open--close pairs. In
+% package mode the standard settings is
+% \begin{verbatim}
+% $ $ \( \)
+% \end{verbatim}
+% \end{variable}
%
-% `Mixed' case conversion may be regarded informally as converting the first
-% character of the \meta{tokens} to upper case and the rest to lower case.
-% However, the process is more complex than this as there are some cases
-% where a single lower case character maps to a special form,
-% for example \texttt{ij} in Dutch which becomes \texttt{IJ}. As such,
-% \cs{tl_mixed_case:n(n)} implement a more sophisticated mapping which
-% accounts for this and for modifying accents on the first letter.
-% Spaces at the start of the \meta{tokens} are ignored when finding the
-% first \enquote{letter} for conversion, while a brace group will terminate
-% this search. For example
+% In addition to this functionality, the case change system will also
+% convert text stored using the \LaTeXe{} \enquote{LICR} approach. This
+% will upper/lower case tokens as implemented for the font encodings
+% \texttt{T1}, \texttt{T2}, \texttt{T5} and \texttt{LGR} (see the behaviour of
+% the \LaTeXe{} command \tn{MakeUppercase}).
+%
+% \enquote{Mixed} case conversion may be regarded informally as converting the
+% first character of the \meta{tokens} to upper case and the rest to lower
+% case. However, the process is more complex than this as there are some
+% situations where a single lower case character maps to a special form, for
+% example \texttt{ij} in Dutch which becomes \texttt{IJ}. As such,
+% \cs{tl_mixed_case:n(n)} implement a more sophisticated mapping which accounts
+% for this and for modifying accents on the first letter. Spaces at the start
+% of the \meta{tokens} are ignored when finding the first \enquote{letter} for
+% conversion.
+% \begin{verbatim}
+% \tl_mixed_case:n { hello~WORLD } % => "Hello world"
+% \tl_mixed_case:n { ~hello~WORLD } % => " Hello world"
+% \tl_mixed_case:n { {hello}~WORLD } % => "{Hello} world"
+% \end{verbatim}
+% When finding the first \enquote{letter} for this process, any content in
+% math mode or covered by \cs{l_tl_case_change_exclude_tl} is ignored.
+%
+% (Note that the Unicode Consortium describe this as \enquote{title case}, but
+% that in English title case applies on a word-by-word basis. The
+% \enquote{mixed} case implemented here is a lower level concept needed for
+% both \enquote{title} and \enquote{sentence} casing of text.)
+%
+% \begin{variable}{\l_tl_mixed_case_ignore_tl}
+% The list of characters to ignore when searching for the first
+% \enquote{letter} in mixed-casing is determined by
+% \cs{l_tl_mixed_change_ignore_tl}. This has the standard setting
% \begin{verbatim}
-% \tl_mixed_case:n { hello~WORLD } % => "Hello world"
-% \tl_mixed_case:n { ~hello~WORLD } % => " Hello world"
-% \tl_mixed_case:n { {hello}~WORLD } % => "{hello} world"
+% ( [ { ` -
% \end{verbatim}
-% where the brace group is retained. (Note that the Unicode Consortium
-% describe this as `title case', but that in English title case applies
-% on a word-by-word basis. The `mixed' case implemented here is a lower
-% level concept needed for both `title' and `sentence' casing of text.)
+% where comparisons are made on a character basis.
+% \end{variable}
%
% As is generally true for \pkg{expl3}, these functions are designed to
% work with Unicode input only. As such, when used with \pdfTeX{}
@@ -869,12 +930,20 @@
% input is left unchanged.
%
% Context-sensitive mappings are enabled: language-dependent cases are
-% discussed below. The \enquote{final sigma} rule for Greek letters is
-% enabled and active for all inputs. It is implemented here in a modified
-% form which takes account of the requirements of the likely real use cases,
-% performance and expandability. Thus a capital sigma will map to a
-% final-sigma if it is followed by a space or one of the characters: ^^A{[(
-% |!'),.:;?]}|. (Feedback on this area is very welcome.)
+% discussed below.
+%
+% \begin{variable}{\l_tl_change_case_after_final_sigma_tl}
+% The \enquote{final sigma} rule for Greek letters is enabled and active for
+% all inputs. It is implemented here in a modified form which takes account
+% of the requirements of the likely real use cases, performance and
+% expandability. A capital sigma will map to a final-sigma if it is followed
+% by a space, the end of the input or one of the characters listed in
+% \cs{l_tl_change_case_after_final_sigma_tl}, which has standard setting
+% \begin{verbatim}
+% ) ] } . : ; , ! ? ' "
+% \end{verbatim}
+% with comparisons are made on a character basis.
+% \end{variable}
%
% Language-sensitive conversions are enabled using the \meta{language}
% argument, and follow Unicode Consortium guidelines. Currently, the
@@ -2990,6 +3059,20 @@
% to allow for all of the special cases. These functions also require the
% appropriate data extracted from the Unicode documentation (either manually
% or automatically), which is covered by \pkg{l3unicode-data}.
+%
+% \begin{macro}[EXP]{\str_case:nVF, \str_case:nvF, \str_case:nvTF}
+% \begin{macro}[EXP]{\tl_if_blank:fTF}
+% \begin{macro}[EXP]{\tl_if_head_eq_catcode:oNTF}
+% Required variants.
+% \begin{macrocode}
+\cs_generate_variant:Nn \str_case:nnF { nV , nv }
+\cs_generate_variant:Nn \str_case:nnTF { nv }
+\cs_generate_variant:Nn \tl_if_blank:nTF { f }
+\cs_generate_variant:Nn \tl_if_head_eq_catcode:nNTF { o }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
%
% \begin{macro}[rEXP]{\tl_lower_case:n, \tl_upper_case:n, \tl_mixed_case:n}
% \begin{macro}[rEXP]{\tl_lower_case:nn, \tl_upper_case:nn, \tl_mixed_case:nn}
@@ -3008,20 +3091,38 @@
% \end{macro}
% \end{macro}
%
-% \begin{macro}[aux]{\@@_change_case:nnn}
-% \begin{macro}[aux]{\@@_change_case_loop:wnn}
-% \begin{macro}[aux]{\@@_change_case_N_type:Nwnn}
-% \begin{macro}[aux]{\@@_change_case_group:nwnn}
-% \begin{macro}[aux]{\@@_change_case_space:wnn}
-% \begin{macro}[aux]{\@@_change_case_char:NNNNNNNNn}
-% \begin{macro}[aux]^^A
+% \begin{macro}[aux, EXP]{\@@_change_case:nnn}
+% \begin{macro}[aux, EXP]{\@@_change_case_loop:wnn}
+% \begin{macro}[aux, EXP]{\@@_change_case_N_type:Nwnn}
+% \begin{macro}[aux, EXP]{\@@_change_case_N_type:NNN}
+% \begin{macro}[aux, EXP]{\@@_change_case_N_type:Nnnn}
+% \begin{macro}[aux, EXP]{\@@_change_case_group:nwnn}
+% \begin{macro}[aux, EXP]{\@@_change_case_space:wnn}
+% \begin{macro}[aux, EXP]{\@@_change_case_char:NNNNNNNNn}
+% \begin{macro}[aux, EXP]{\@@_change_case_math:NNNn}
+% \begin{macro}[aux, EXP]{\@@_change_case_math_loop:wNN}
+% \begin{macro}[aux, EXP]{\@@_change_case_math:NwNN}
+% \begin{macro}[aux, EXP]{\@@_change_case_math_group:nwNN}
+% \begin{macro}[aux, EXP]{\@@_change_case_math_space:wNN}
+% \begin{macro}[aux, EXP]{\@@_change_case_cs:Nnnn}
+% \begin{macro}[aux, EXP]{\@@_change_case_cs:nNnnn}
+% \begin{macro}[aux, EXP]{\@@_change_case_cs_three:w}
+% \begin{macro}[aux, EXP]{\@@_change_case_cs_four:w}
+% \begin{macro}[aux, EXP]{\@@_change_case_cs:Nnnnn}
+% \begin{macro}[aux, EXP]{\@@_change_case_cs_cyr:NnNNNNw}
+% \begin{macro}[aux, EXP]{\@@_change_case_cs:N}
+% \begin{macro}[aux, EXP]{\@@_change_case_cs:NN}
+% \begin{macro}[aux, EXP]{\@@_change_case_cs:NNn}
+% \begin{macro}[aux, EXP]{\@@_change_case_cs_expand:N}
+% \begin{macro}[aux, EXP]{\@@_change_case_cs_expand:NN}
+% \begin{macro}[aux, EXP]^^A
% {
% \@@_change_case_lower_sigma:Nnn ,
% \@@_change_case_upper_sigma:Nnn ,
% \@@_change_case_mixed_sigma:Nnn
% }
% \begin{macro}[aux]{\@@_change_case_lower_sigma:Nw}
-% \begin{macro}[aux]{\@@_change_case_lower_sigma_loop:Nw}
+% \begin{macro}[aux]{\@@_change_case_lower_sigma_loop:NN}
% The mechanism for the core conversion of case is based on the approach used
% in \cs{@@_act:NNNnn}. Thus the idea is to use a loop which is will grab
% the entire token list plus a quark: the latter is used as an end marker and
@@ -3031,9 +3132,11 @@
% treatment). In contrast to \cs{@@_act:NNNnn}, there is no need for this
% process to be \texttt{f}-type expandable: things are done using only
% \texttt{x}-type requirements. Also, for \enquote{normal} tokens there is
-% a bit more work to do here: to allow selection of case matches using
-% character code, it's important that control sequences are filtered out
-% before doing the lookup.
+% a bit more work to do here. To allow math mode escape for paired tokens,
+% there is first a search to see if we've picked up on of the possible
+% openers. If we have, there is a separate route to follow. Otherwise
+% we need to choose between control sequences and characters: only the
+% latter allow character code to be used for the conversion lookup.
% \begin{macrocode}
\cs_new:Npn \@@_change_case:nnn #1#2#3
{
@@ -3051,11 +3154,40 @@
}
#1 \q_recursion_stop
}
-\cs_new:Npn \@@_change_case_N_type:Nwnn #1#2 \q_recursion_stop #3#4
+\cs_new:Npn \@@_change_case_N_type:Nwnn #1#2 \q_recursion_stop
+ {
+ \quark_if_recursion_tail_stop_do:Nn #1 { \use_none:nnn }
+ \exp_after:wN \@@_change_case_N_type:NNN
+ \exp_after:wN #1 \l_tl_case_change_math_tl
+ \q_recursion_tail ? \q_recursion_stop {#2}
+ }
+\cs_new:Npn \@@_change_case_N_type:NNN #1#2#3
+ {
+ \quark_if_recursion_tail_stop_do:Nn #2
+ { \@@_change_case_N_type:Nnnn #1 }
+ \token_if_eq_meaning:NNTF #1 #2
+ {
+ \use_i_delimit_by_q_recursion_stop:nw
+ {
+ \@@_change_case_math:NNNn #1 #3
+ \@@_change_case_loop:wnn
+ }
+ }
+ { \@@_change_case_N_type:NNN #1 }
+ }
+\cs_new:Npn \@@_change_case_N_type:Nnnn #1#2#3#4
{
- \quark_if_recursion_tail_stop_do:Nn #1 { \use_none:nn }
\token_if_cs:NTF #1
- { \exp_not:N #1 }
+%<*initex>
+ { \@@_change_case_cs:N #1 }
+%</initex>
+%<*package>
+ {
+ \@@_change_case_cs:Nnnn #1 {#3}
+ { }
+ { \@@_change_case_cs:N #1 }
+ }
+%</package>
{
\cs_if_exist_use:cF { @@_change_case_ #3 _ #4 :Nnn }
{ \use_iii:nnn }
@@ -3071,10 +3203,13 @@
}
\@@_change_case_loop:wnn #2 \q_recursion_stop {#3} {#4}
}
-\cs_new:Npn \@@_change_case_group:nwnn #1#2 \q_recursion_stop
+\cs_new:Npn \@@_change_case_group:nwnn #1#2 \q_recursion_stop #3#4
{
- { \exp_not:n {#1} }
- \@@_change_case_loop:wnn #2 \q_recursion_stop
+ {
+ \@@_change_case_loop:wnn #1
+ \q_recursion_tail \q_recursion_stop {#3} {#4}
+ }
+ \@@_change_case_loop:wnn #2 \q_recursion_stop {#3} {#4}
}
\exp_last_unbraced:NNo \cs_new:Npn \@@_change_case_space:wnn \c_space_tl
{
@@ -3082,14 +3217,201 @@
\@@_change_case_loop:wnn
}
% \end{macrocode}
-% Actually look for the char in the appropriate table.
+% Actually look for the char in the appropriate table. This code is a
+% compromise between performance and hash name use. The fastest approach
+% would be to create one token list for each char (and could cover special
+% case control sequences too) and use a hash table lookup. That is
+% quite \enquote{expensive}, so instead a \cs{str_case:nvF}
+% approach is used. One big list would be most efficient in name usage
+% but then gets slow for entries later in the list. Doing a split into
+% $100$ bins falls somewhere between the two extremes.
% \begin{macrocode}
-\cs_new:Npn \__tl_change_case_char:NNNNNNNNn #1#2#3#4#5#6#7#8#9
+\cs_new:Npn \@@_change_case_char:NNNNNNNNn #1#2#3#4#5#6#7#8#9
{
- \exp_args:NNv \str_case:nnF #8
+ \str_case:nvF #8
{ c_@@_ #9 _ #6 _X_ #7 _tl }
- { \exp_not:N #8 }
+ { \exp_not:n {#8} }
+ }
+% \end{macrocode}
+% When dealing with a token that can open math mode, the idea is to
+% loop to find the closing token and to preserve everything as-is.
+% The loop therefore looks much the same as the normal one, but with
+% a check to look for end-of-math mode. Once that is found or once we
+% hit the end of the input, the \enquote{normal} loop is restarted. That
+% is done so that the code here works for both standard case changing and
+% for the mixed case version: the clean up needed is different but the
+% loop functions passes (as |#4| to \cs{@@_change_case_math:NNNn}) deal
+% with that.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_math:NNNn #1#2#3#4
+ {
+ \exp_not:n {#1}
+ \@@_change_case_math_loop:wNN #4 \q_recursion_stop #2 #3
+ }
+\cs_new:Npn \@@_change_case_math_loop:wNN #1 \q_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#1}
+ { \@@_change_case_math:NwNN }
+ {
+ \tl_if_head_is_group:nTF {#1}
+ { \@@_change_case_math_group:nwNN }
+ { \@@_change_case_math_space:wNN }
+ }
+ #1 \q_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_math:NwNN #1#2 \q_recursion_stop #3#4
+ {
+ \token_if_eq_meaning:NNTF \q_recursion_tail #1
+ { #4 \q_recursion_tail \q_recursion_stop }
+ {
+ \exp_not:n {#1}
+ \token_if_eq_meaning:NNTF #1 #3
+ { #4 #2 \q_recursion_stop }
+ { \@@_change_case_math_loop:wNN #2 \q_recursion_stop #3#4 }
+ }
+ }
+\cs_new:Npn \@@_change_case_math_group:nwNN #1#2 \q_recursion_stop
+ {
+ { \exp_not:n {#1} }
+ \@@_change_case_math_loop:wNN #2 \q_recursion_stop
+ }
+\exp_last_unbraced:NNo \cs_new:Npn \@@_change_case_math_space:wNN \c_space_tl
+ {
+ \c_space_tl
+ \@@_change_case_math_loop:wNN
+ }
+% \end{macrocode}
+% If a control sequence has been given as the argument and it is not on
+% the list of those with an argument to examine, the other possibility
+% is that it is a character represented as a command such as |\aa|. To
+% deal with that there is a need again to balance performance against
+% name use. For \LaTeXe{} the list of possible special cases is quite
+% long (there are around $100$ for Cyrillic alone) so a single long
+% \cs{str_case:nvF} is inefficient. On the other hand, using one
+% tl per special case would use a lot of names. The balance here is
+% to split up the special cases by type and for Cyrillic to further
+% subdivide. This works as there are various cases:
+% \begin{itemize}
+% \item Short (one or two letter) names for special Latin characters
+% \item Cyrillic names, all starting |cyr|
+% \item Greek names, all starting |text|
+% \item Greek accents, all starting |acc| and only applicable
+% for upper casing
+% \item Other cases (\enquote{bits and pieces})
+% \end{itemize}
+% Thus a split can be carried out by converting the control sequence to
+% a string then examining the first four characters. For Cyrillic, the
+% fourth character can be used for a second split based on the character
+% code.
+%
+% Note that as this is dependent on \LaTeXe{}, in format mode the code
+% goes straight to the final phase of handling control sequences.
+% \begin{macrocode}
+%<*package>
+\cs_new:Npn \@@_change_case_cs:Nnnn #1#2
+ {
+ \exp_args:Nf \@@_change_case_cs:nNnnn
+ { \cs_to_str:N #1 } #1 {#2}
+ }
+\cs_new:Npn \@@_change_case_cs:nNnnn #1#2#3
+ {
+ \tl_if_head_eq_catcode:oNTF { \use_none:nnn #1 a a a a } a
+ { \@@_change_case_cs_type:Nnnnn #2 { latin } {#3} }
+ {
+ \str_if_eq_x:nnTF
+ { \@@_change_case_cs_three:w #1 \q_nil }
+ { \str_if_eq:nnTF {#3} { lower } { CYR } { cyr } }
+ { \@@_change_case_cs_cyr:NnNNNNw #2 {#3} #1 \q_stop }
+ {
+ \str_if_eq_x:nnTF
+ { \@@_change_case_cs_three:w #1 \q_nil }
+ { acc }
+ { \@@_change_case_cs_type:Nnnnn #2 { acc } {#3} }
+ {
+ \str_if_eq_x:nnTF
+ { \@@_change_case_cs_four:w #1 \q_nil }
+ { text }
+ { \@@_change_case_cs_type:Nnnnn #2 { greek } {#3} }
+ { \@@_change_case_cs_type:Nnnnn #2 { misc } {#3} }
+ }
+ }
+ }
+ }
+\cs_new:Npn \@@_change_case_cs_three:w #1#2#3#4 \q_nil { #1#2#3 }
+\cs_new:Npn \@@_change_case_cs_four:w #1#2#3#4#5 \q_nil { #1#2#3#4 }
+\cs_new:Npn \@@_change_case_cs_type:Nnnnn #1#2#3
+ {
+ \str_case:nvTF #1
+ { c_@@_change_case_ #2 _ #3 _ tl }
+ }
+\cs_new:Npn \@@_change_case_cs_cyr:NnNNNNw #1#2#3#4#5#6#7 \q_stop
+ {
+ \@@_change_case_cs_type:Nnnnn #1
+ { cyrillic }
+ {
+ #2 _
+ \int_to_roman:n
+ {
+ 1 +
+ \int_div_truncate:nn
+ {
+ `#6 - \str_if_eq:nnTF {#2} { lower } { `A } { `a }
+ }
+ { 7 }
+ }
+ }
}
+%</package>
+% \end{macrocode}
+% To deal with a control sequence there is first a need to test if it is
+% on the list which indicate that case changing should be skipped. That's
+% done using a loop as we do \cs{tl_if_in:N(TF)} expandably. If a hit is
+% found then the argument is grabbed: that comes \emph{after} the loop
+% function which is therefore also picked up and rearranged. This means
+% that the same code can be used for mixed case, where an escaped part
+% is ignored for the purposes of identifying a leading letter.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_cs:N #1
+ {
+ \exp_after:wN \@@_change_case_cs:NN
+ \exp_after:wN #1 \l_tl_case_change_exclude_tl
+ \q_recursion_tail \q_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_cs:NN #1#2
+ {
+ \quark_if_recursion_tail_stop_do:Nn #2
+ { \@@_change_case_cs_expand:N #1 }
+ \str_if_eq:nnTF {#1} {#2}
+ {
+ \use_i_delimit_by_q_recursion_stop:nw
+ { \@@_change_case_cs:NNn #1 }
+ }
+ { \@@_change_case_cs:NN #1 }
+ }
+\cs_new:Npn \@@_change_case_cs:NNn #1#2#3
+ {
+ \exp_not:n { #1 {#3} }
+ #2
+ }
+% \end{macrocode}
+% When a control sequence is not on the exclude list the other test if
+% to see if it is expandable. Once again, if there is a hit then the loop
+% function is grabbed as part of the clean-up and reinserted before the
+% now expanded material.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_cs_expand:N #1
+ {
+ \bool_if:nTF
+ {
+ \token_if_expandable_p:N #1
+ && ! \token_if_protected_macro_p:N #1
+ && ! \token_if_protected_long_macro_p:N #1
+ }
+ { \@@_change_case_cs_expand:NN #1 }
+ { \exp_not:n {#1} }
+ }
+\cs_new:Npn \@@_change_case_cs_expand:NN #1#2
+ { \exp_after:wN #2 #1 }
% \end{macrocode}
% If the current char is an upper case sigma, the a check is made on the next
% item in the input. If it is another \texttt{N}-type token then further tests
@@ -3128,18 +3450,18 @@
{ \c_@@_std_sigma_tl }
}
{
- \exp_after:wN \@@_change_case_lower_sigma_loop:Nw
- \exp_after:wN #1 \c_@@_after_final_sigma_clist
- , \q_recursion_tail , \q_recursion_stop
+ \exp_after:wN \@@_change_case_lower_sigma_loop:NN
+ \exp_after:wN #1 \l_tl_case_change_after_final_sigma_tl
+ \q_recursion_tail \q_recursion_stop
}
}
-\cs_new:Npn \@@_change_case_lower_sigma_loop:Nw #1#2 ,
+\cs_new:Npn \@@_change_case_lower_sigma_loop:NN #1#2
{
\quark_if_recursion_tail_stop_do:nn {#2}
{ \c_@@_std_sigma_tl }
- \int_compare:nNnT { `#1 } = { "#2 }
+ \int_compare:nNnT { `#1 } = { `#2 }
{ \use_i_delimit_by_q_recursion_stop:nw { \c_@@_final_sigma_tl } }
- \@@_change_case_lower_sigma_loop:Nw #1
+ \@@_change_case_lower_sigma_loop:NN #1
}
\cs_new_eq:NN \@@_change_case_upper_sigma:Nnn \use_iii:nnn
\cs_new_eq:NN \@@_change_case_mixed_sigma:Nnn \use_iii:nnn
@@ -3153,14 +3475,34 @@
% \end{macro}
% \end{macro}
% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
%
-% \begin{macro}[aux]{\@@_mixed_case:nn}
-% \begin{macro}[aux]{\@@_mixed_case_loop:wn}
-% \begin{macro}[aux]{\@@_mixed_case_N_type:Nwn}
-% \begin{macro}[aux]{\@@_mixed_case_skip:Nwn}
-% \begin{macro}[aux]{\@@_mixed_case_skip_tidy:nNwn}
-% \begin{macro}[aux]{\@@_mixed_case_group:nwn}
-% \begin{macro}[aux]{\@@_mixed_case_space:wn}
+% \begin{macro}[aux, EXP]{\@@_mixed_case:nn}
+% \begin{macro}[aux, EXP]{\@@_mixed_case_loop:wn}
+% \begin{macro}[aux, EXP]{\@@_mixed_case_N_type:Nwn}
+% \begin{macro}[aux, EXP]{\@@_mixed_case_N_type:NNN}
+% \begin{macro}[aux, EXP]{\@@_mixed_case_N_type:Nnn}
+% \begin{macro}[aux, EXP]{\@@_mixed_case_skip:Nwn}
+% \begin{macro}[aux, EXP]{\@@_mixed_case_skip_tidy:nNwn}
+% \begin{macro}[aux, EXP]{\@@_mixed_case_group:nwn}
+% \begin{macro}[aux, EXP]{\@@_mixed_case_space:wn}
% Mixed (title) casing needs an entire set of functions to itself. These are
% more or less the same as those for general case changes but the
% requirements here are subtly different. What is needed is a loop which
@@ -3191,11 +3533,49 @@
}
#1 \q_recursion_stop
}
-\cs_new:Npn \@@_mixed_case_N_type:Nwn #1#2 \q_recursion_stop #3
+\cs_new:Npn \@@_mixed_case_N_type:Nwn #1#2 \q_recursion_stop
{
\quark_if_recursion_tail_stop_do:Nn #1 { \use_none:nn }
+ \exp_after:wN \@@_mixed_case_N_type:NNN
+ \exp_after:wN #1 \l_tl_case_change_math_tl
+ \q_recursion_tail ? \q_recursion_stop {#2}
+ }
+\cs_new:Npn \@@_mixed_case_N_type:NNN #1#2#3
+ {
+ \quark_if_recursion_tail_stop_do:Nn #2
+ { \@@_mixed_case_N_type:Nnn #1 }
+ \token_if_eq_meaning:NNTF #1 #2
+ {
+ \use_i_delimit_by_q_recursion_stop:nw
+ {
+ \@@_change_case_math:NNNn #1 #3
+ \@@_mixed_case_loop:wn
+ }
+ }
+ { \@@_mixed_case_N_type:NNN #1 }
+ }
+\cs_new:Npn \@@_mixed_case_N_type:Nnn #1#2#3
+ {
\token_if_cs:NTF #1
- { \exp_not:N #1 }
+%<*initex>
+ {
+ \@@_change_case_cs:N #1
+ \@@_mixed_case_loop:wn #2 \q_recursion_stop {#3}
+ }
+%</initex>
+%<*package>
+ {
+ \@@_change_case_cs:Nnnn #1 { upper }
+ {
+ \@@_change_case_loop:wnn
+ #2 \q_recursion_stop { lower } {#3}
+ }
+ {
+ \@@_change_case_cs:N #1
+ \@@_mixed_case_loop:wn #2 \q_recursion_stop {#3}
+ }
+ }
+%</package>
{
\cs_if_exist_use:cF { @@_change_case_mixed_ #3 :Nnn }
{
@@ -3204,11 +3584,11 @@
}
#1 {#2}
{
- \exp_after:wN \@@_mixed_case_skip:Nwn \exp_after:wN #1
- \c__tl_mixed_skip_clist , \q_recursion_tail ,
- \q_recursion_stop
+ \exp_after:wN \@@_mixed_case_skip:NNn
+ \exp_after:wN #1 \l_tl_mixed_case_ignore_tl
+ \q_recursion_tail \q_recursion_stop
{
- \exp_args:NNV \str_case:nnF #1 \c_@@_mixed_exceptions_tl
+ \str_case:nVF #1 \c_@@_mixed_exceptions_tl
{
\exp_after:wN \@@_change_case_char:NNNNNNNNn
\int_use:N
@@ -3217,8 +3597,8 @@
}
}
}
+ \@@_change_case_loop:wnn #2 \q_recursion_stop { lower } {#3}
}
- \@@_change_case_loop:wnn #2 \q_recursion_stop { lower } {#3}
}
% \end{macrocode}
% Looking for chars to skip when title casing uses the standard \enquote{loop
@@ -3226,12 +3606,16 @@
% to do: retain the char and switch the looping system to stick with the
% title loop rather than the lower case one. That means swapping an
% auxiliary and removing a trailing |{ lower }|, which is easiest to do
-% with a dedicated function.
+% with a dedicated function. When dealing with a brace group there is a
+% need to test if there is any content in it: that affects whether to keep
+% looking for the first character or not after the group. If there is
+% anything non-expandable at all in it then that will be treated as the
+% first \enquote{character}.
% \begin{macrocode}
-\cs_new:Npn \@@_mixed_case_skip:Nwn #1#2 ,
+\cs_new:Npn \@@_mixed_case_skip:NNn #1#2
{
\quark_if_recursion_tail_stop_do:nn {#2} { \use:n }
- \int_compare:nNnT { `#1 } = { "#2 }
+ \int_compare:nNnT { `#1 } = { `#2 }
{
\use_i_delimit_by_q_recursion_stop:nw
{
@@ -3239,16 +3623,23 @@
\@@_mixed_case_skip_tidy:nNwn
}
}
- \@@_mixed_case_skip:Nwn #1
+ \@@_mixed_case_skip:NNn #1
}
\cs_new:Npn \@@_mixed_case_skip_tidy:nNwn #1#2#3 \q_recursion_stop #4
{
\@@_mixed_case_loop:wn #3 \q_recursion_stop
}
-\cs_new:Npn \@@_mixed_case_group:nwn #1#2 \q_recursion_stop
+\cs_new:Npn \@@_mixed_case_group:nwn #1#2 \q_recursion_stop #3
{
- { \exp_not:n {#1} }
- \@@_change_case_loop:wnn #2 \q_recursion_stop { lower }
+ \tl_if_blank:fTF {#1}
+ { { } \@@_mixed_case_loop:wn #2 \q_recursion_stop {#3} }
+ {
+ {
+ \@@_mixed_case_loop:wn #1
+ \q_recursion_tail \q_recursion_stop {#3}
+ }
+ \@@_change_case_loop:wnn #2 \q_recursion_stop { lower } {#3}
+ }
}
\exp_last_unbraced:NNo \cs_new:Npn \@@_mixed_case_space:wn \c_space_tl
{
@@ -3263,6 +3654,8 @@
% \end{macro}
% \end{macro}
% \end{macro}
+% \end{macro}
+% \end{macro}
%
% \begin{macro}[aux]^^A
% {
@@ -3348,7 +3741,7 @@
% \begin{macrocode}
\cs_new:Npn \@@_change_case_lower_lt:Nnn #1#2#3
{
- \exp_args:NNV \str_case:nnF #1 \c_@@_accents_lt_tl
+ \str_case:nVF #1 \c_@@_accents_lt_tl
{
#3
\bool_if:nT
@@ -3449,6 +3842,351 @@
% \end{macro}
% \end{macro}
%
+% \begin{variable}{\l_tl_case_change_math_tl}
+% The list of token pairs which are treated as math mode and so
+% not case changed.
+% \begin{macrocode}
+\tl_new:N \l_tl_case_change_math_tl
+%<*package>
+\tl_set:Nn \l_tl_case_change_math_tl
+ { $ $ \( \) }
+%</package>
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_tl_case_change_exclude_tl}
+% The list of commands for which an argument is not case changed.
+% \begin{macrocode}
+\tl_new:N \l_tl_case_change_exclude_tl
+%<*package>
+\tl_set:Nn \l_tl_case_change_exclude_tl
+ { \cite \ensuremath \label \ref }
+%</package>
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_tl_case_change_after_final_sigma_tl}
+% Characters coming after a final sigma.
+% \begin{macrocode}
+\tl_new:N \l_tl_case_change_after_final_sigma_tl
+\tl_set:Nx \l_tl_case_change_after_final_sigma_tl
+ { % (
+ ) % [
+ ] % \{
+ \cs_to_str:N \}
+ . : ; ,
+ ! ? ' "
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_tl_mixed_case_ignore_tl}
+% Characters to skip over when finding the first letter in a word to be
+% mixed cased.
+% \begin{macrocode}
+\tl_new:N \l_tl_mixed_case_ignore_tl
+\tl_set:Nx \l_tl_mixed_case_ignore_tl
+ {
+ ( % )
+ [ % ]
+ \cs_to_str:N \{ % \}
+ `
+ -
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}
+% {
+% \c_@@_change_case_latin_upper_tl ,
+% \c_@@_change_case_latin_lower_tl ,
+% \c_@@_change_case_cyrillic_upper_i_tl ,
+% \c_@@_change_case_cyrillic_upper_ii_tl ,
+% \c_@@_change_case_cyrillic_upper_iii_tl ,
+% \c_@@_change_case_cyrillic_upper_iv_tl ,
+% \c_@@_change_case_cyrillic_lower_i_tl ,
+% \c_@@_change_case_cyrillic_lower_ii_tl ,
+% \c_@@_change_case_cyrillic_lower_iii_tl ,
+% \c_@@_change_case_cyrillic_lower_iv_tl ,
+% \c_@@_change_case_greek_upper_tl ,
+% \c_@@_change_case_greek_lower_tl ,
+% \c_@@_change_case_acc_upper_tl ,
+% \c_@@_change_case_acc_upper_tl ,
+% \c_@@_change_case_misc_upper_tl ,
+% \c_@@_change_case_misc_lower_tl
+% }
+% The data for case changing control sequences representing characters
+% is stored such that further expansion is inhibited. This is required
+% as many of the \LaTeXe{} commands are fragile and so will fail inside
+% an \texttt{x}-type expansion without this precaution. As such, there is
+% a bit of set up to deal with the various requirements here: upper and
+% lower case mappings are not identical so special end cases are needed
+% to get everything set up correctly with minimal repetition.
+% \begin{macrocode}
+\group_begin:
+ \cs_set_protected:Npn \@@_change_case_setup:nnnn #1#2#3#4
+ {
+ \tl_const:cx { c_@@_change_case_ #1 _upper #2 _tl }
+ {
+ \@@_change_case_map:NN
+ #3 \q_recursion_tail ? \q_recursion_stop
+ }
+ \tl_const:cx { c_@@_change_case_ #1 _lower #2 _tl }
+ {
+ \@@_change_case_map:NNN #4
+ #3 \q_recursion_tail ? \q_recursion_stop
+ }
+ }
+ \cs_set:Npn \@@_change_case_map:NN #1#2
+ {
+ \quark_if_recursion_tail_stop:N #1
+%<*initex>
+ \exp_not:N #1 \exp_not:N #2
+%</initex>
+%<*package>
+ \exp_not:N #1 \exp_not:n { { \exp_not:n {#2} } }
+%</package>
+ \@@_change_case_map:NN
+ }
+ \cs_set:Npn \@@_change_case_map:NNN #1#2#3
+ {
+ \str_if_eq:nnT {#1} {#2}
+ { \use_none_delimit_by_q_recursion_stop:w }
+%<*initex>
+ \exp_not:N #3 \exp_not:N #2
+%</initex>
+%<*package>
+ \exp_not:N #3 \exp_not:n { { \exp_not:n {#2} } }
+%</package>
+ \@@_change_case_map:NNN #1
+ }
+ \@@_change_case_setup:nnnn
+ { latin }
+ { }
+ {
+ \aa \AA
+ \ae \AE
+ \dh \DH
+ \dj \DJ
+ \l \L
+ \ng \NG
+ \o \O
+ \oe \OE
+ \ss \SS
+ \th \TH
+ \i I
+ \j J
+ }
+ { \i }
+%<*package>
+ \@@_change_case_setup:nnnn
+ { cyrillic }
+ { _i }
+ {
+ \cyra \CYRA
+ \cyrabhch \CYRABHCH
+ \cyrabhchdsc \CYRABHCHDSC
+ \cyrabhdze \CYRABHDZE
+ \cyrabhha \CYRABHHA
+ \cyrae \CYRAE
+ \cyrb \CYRB
+ \cyrbyus \CYRBYUS
+ \cyrc \CYRC
+ \cyrch \CYRCH
+ \cyrchldsc \CYRCHLDSC
+ \cyrchrdsc \CYRCHRDSC
+ \cyrchvcrs \CYRCHVCRS
+ \cyrd \CYRD
+ \cyrdelta \CYRDELTA
+ \cyrdje \CYRDJE
+ \cyrdze \CYRDZE
+ \cyrdzhe \CYRDZHE
+ \cyre \CYRE
+ \cyreps \CYREPS
+ \cyrerev \CYREREV
+ \cyrery \CYRERY
+ \cyrf \CYRF
+ \cyrfita \CYRFITA
+ \cyrg \CYRG
+ \cyrgdsc \CYRGDSC
+ \cyrgdschcrs \CYRGDSCHCRS
+ \cyrghcrs \CYRGHCRS
+ \cyrghk \CYRGHK
+ \cyrgup \CYRGUP
+ }
+ { \q_recursion_tail }
+ \@@_change_case_setup:nnnn
+ { cyrillic }
+ { _ii }
+ {
+ \cyrh \CYRH
+ \cyrhdsc \CYRHDSC
+ \cyrhhcrs \CYRHHCRS
+ \cyrhhk \CYRHHK
+ \cyrhrdsn \CYRHRDSN
+ \cyri \CYRI
+ \cyrie \CYRIE
+ \cyrii \CYRII
+ \cyrishrt \CYRISHRT
+ \cyrishrtdsc \CYRISHRTDSC
+ \cyrizh \CYRIZH
+ \cyrje \CYRJE
+ \cyrk \CYRK
+ \cyrkbeak \CYRKBEAK
+ \cyrkdsc \CYRKDSC
+ \cyrkhcrs \CYRKHCRS
+ \cyrkhk \CYRKHK
+ \cyrkvcrs \CYRKVCRS
+ \cyrl \CYRL
+ \cyrldsc \CYRLDSC
+ \cyrlhk \CYRLHK
+ \cyrlje \CYRLJE
+ \cyrm \CYRM
+ \cyrmdsc \CYRMDSC
+ \cyrmhk \CYRMHK
+ \cyrn \CYRN
+ \cyrndsc \CYRNDSC
+ \cyrng \CYRNG
+ \cyrnhk \CYRNHK
+ \cyrnje \CYRNJE
+ \cyrnlhk \CYRNLHK
+ }
+ { \q_recursion_tail }
+ \@@_change_case_setup:nnnn
+ { cyrillic }
+ { _iii }
+ {
+ \cyro \CYRO
+ \cyrotld \CYROTLD
+ \cyrp \CYRP
+ \cyrphk \CYRPHK
+ \cyrq \CYRQ
+ \cyrr \CYRR
+ \cyrrdsc \CYRRDSC
+ \cyrrhk \CYRRHK
+ \cyrrtick \CYRRTICK
+ \cyrs \CYRS
+ \cyrsacrs \CYRSACRS
+ \cyrschwa \CYRSCHWA
+ \cyrsdsc \CYRSDSC
+ \cyrsemisftsn \CYRSEMISFTSN
+ \cyrsftsn \CYRSFTSN
+ \cyrsh \CYRSH
+ \cyrshch \CYRSHCH
+ \cyrshha \CYRSHHA
+ \cyrt \CYRT
+ \cyrtdsc \CYRTDSC
+ \cyrtetse \CYRTETSE
+ \cyrtshe \CYRTSHE
+ \cyru \CYRU
+ \cyrushrt \CYRUSHRT
+ }
+ { \q_recursion_tail }
+ \@@_change_case_setup:nnnn
+ { cyrillic }
+ { _iv }
+ {
+ \cyrv \CYRV
+ \cyrw \CYRW
+ \cyry \CYRY
+ \cyrya \CYRYA
+ \cyryat \CYRYAT
+ \cyryhcrs \CYRYHCRS
+ \cyryi \CYRYI
+ \cyryo \CYRYO
+ \cyryu \CYRYU
+ \cyrz \CYRZ
+ \cyrzdsc \CYRZDSC
+ \cyrzh \CYRZH
+ \cyrzhdsc \CYRZHDSC
+ }
+ { \q_recursion_tail }
+ \@@_change_case_setup:nnnn
+ { greek }
+ { }
+ {
+ \textalpha \textAlpha
+ \textbeta \textBeta
+ \textchi \textChi
+ \textdelta \textDelta
+ \textdigamma \textDigamma
+ \texteta \textEta
+ \textepsilon \textEpsilon
+ \textgamma \textGamma
+ \textiota \textIota
+ \textkappa \textKappa
+ \textlambda \textLambda
+ \textmu \textMu
+ \textnu \textNu
+ \textomega \textOmega
+ \textomicron \textOmicron
+ \textphi \textPhi
+ \textpi \textPi
+ \textpsi \textPsi
+ \textqoppa \textQoppa
+ \textrho \textRho
+ \textsampi \textSampi
+ \textautosigma \textSigma
+ \textstigma \textStigma
+ \texttheta \textTheta
+ \texttau \textTau
+ \textupsilon \textUpsilon
+ \textxi \textXi
+ \textzeta \textZeta
+ \textsigma \textSigma
+ \textvarsigma \textSigma
+ \textvarstigma \textStigma
+ }
+ { \textsigma }
+ \tl_const:Nn \c_@@_change_case_acc_upper_tl
+ {
+ \accdasia { \exp_not:n { \LGR@accdropped } }
+ \accdasiaoxia { \exp_not:n { \LGR@hiatus } }
+ \accdasiavaria { \exp_not:n { \LGR@accdropped } }
+ \accdasiaperispomeni { \exp_not:n { \LGR@accdropped } }
+ \accpsili { \exp_not:n { \LGR@hiatus } }
+ \accpsilioxia { \exp_not:n { \LGR@hiatus } }
+ \accpsilivaria { \exp_not:n { \LGR@hiatus } }
+ \accpsiliperispomeni { \exp_not:n { \LGR@accdropped } }
+ \acctonos { \exp_not:n { \LGR@hiatus } }
+ \accvaria { \exp_not:n { \LGR@accdropped } }
+ \accdialytikatonos { \exp_not:n { \LGR@accDialytika } }
+ \accdialytikavaria { \exp_not:n { \LGR@accDialytika } }
+ \accdialytikaperispomeni { \exp_not:n { \LGR@accDialytika } }
+ \accperispomeni { \exp_not:n { \LGR@accdropped } }
+ }
+ \tl_const:Nn \c_@@_change_case_acc_lower_tl { }
+ \tl_const:Nn \c_@@_change_case_misc_upper_tl
+ {
+ \ypogegrammeni { \exp_not:n { \prosgegrammeni } }
+ \abreve { \exp_not:n { \Abreve } }
+ \acircumflex { \exp_not:n { \Acircumflex } }
+ \ecircumflex { \exp_not:n { \Ecircumflex } }
+ \ocircumflex { \exp_not:n { \Ocircumflex } }
+ \ohorn { \exp_not:n { \Ohorn } }
+ \uhorn { \exp_not:n { \Uhorn } }
+ }
+ \tl_const:Nn \c_@@_change_case_misc_lower_tl
+ {
+ \prosgegrammeni { \exp_not:n { \ypogegrammeni } }
+ \Abreve { \exp_not:n { \abreve } }
+ \Acircumflex { \exp_not:n { \acircumflex } }
+ \Ecircumflex { \exp_not:n { \ecircumflex } }
+ \Ocircumflex { \exp_not:n { \ocircumflex } }
+ \Ohorn { \exp_not:n { \ohorn } }
+ \Uhorn { \exp_not:n { \uhorn } }
+ \ABREVE { \exp_not:n { \abreve } }
+ \ACIRCUMFLEX { \exp_not:n { \acircumflex } }
+ \ECIRCUMFLEX { \exp_not:n { \ecircumflex } }
+ \OCIRCUMFLEX { \exp_not:n { \ocircumflex } }
+ \OHORN { \exp_not:n { \ohorn } }
+ \UHORN { \exp_not:n { \uhorn } }
+ }
+%</package>
+\group_end:
+% \end{macrocode}
+% \end{variable}
+%
% \begin{macro}{\tl_log:N, \tl_log:c}
% Showing token list variables in the log file is done after checking
% that the variable is defined.